Preamble

library(here)
here() starts at /Users/benarnold/quantAb-annecy
here()
[1] "/Users/benarnold/quantAb-annecy"
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.4
✔ tidyr   0.8.0     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ───────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(mgcv)
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:dplyr’:

    collapse

This is mgcv 1.8-23. For overview type 'help("mgcv-package")'.
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
# set up for parallel computing
# configure for a laptop (use only 3 cores)
library(foreach)

Attaching package: ‘foreach’

The following objects are masked from ‘package:purrr’:

    accumulate, when
library(doParallel)
Loading required package: iterators
Loading required package: parallel
registerDoParallel(cores=3)
# grab some colors for plotting
ggplotcols <- scales::hue_pal()(3)
# bright color blind palette:  https://personal.sron.nl/~pault/ 
cblack <- "#000004FF"
cblue <- "#3366AA"
cteal <- "#11AA99"
cgreen <- "#66AA55"
cchartr <- "#CCCC55"
cmagent <- "#992288"
cred <- "#EE3333"
corange <- "#EEA722"
cyellow <- "#FFEE33"
cgrey <- "#777777"
pcols <- c(ggplotcols,corange)

Load Mbita Kenya antibody measurements

Note: at this time these data are not yet publicly available. They are from this study: Won KY, Kanyi HM, Mwende FM, Wiegand RE, Brook Goodhew E, Priest JW, et al. Multiplex Serologic Assessment of Schistosomiasis in Western Kenya: Antibody Responses in Preschool Age Children as a Measure of Reduced Transmission. Am J Trop Med Hyg. 2017; 16–0665. https://www.ncbi.nlm.nih.gov/pubmed/28719280

d <- readRDS(here("data","mbita_psac.rds"))
# create age strata
d <- d %>%
  select(year,community=vid,
         pid,age=agey,
         asc_epg,sm_epg,
         ashb,sea,msp1,vsp5) %>%
  mutate(agecat = cut(age,breaks=c(0,1,2,3,4,6),labels=c("<1 year","1 year","2 years","3 years","4 years")))
# reshape KK data to long format
# the eggs per gram (EPG) measures are grouped by "antigen" 
# (a wrong label) just to make them easy to merge back to the antibody data
dlkk <- d %>%
  select(year,community,pid,asc_epg,sm_epg,) %>%
  gather(key=antigen,value=epg,-year,-community,-pid) %>%
  mutate(antigen = factor(antigen,levels=c("asc_epg","sm_epg"),labels=c("ashb","sea"))) %>%
  group_by(year,community,antigen) %>%
  arrange(year,community,antigen)
# reshape antibody data to long format
dl <- d %>%
  select(year,community,pid,age,agecat,ashb,sea,msp1,vsp5) %>%
  gather(key=antigen,value=mfi,-year,-community,-pid,-age,-agecat) %>%
  mutate(antigen = factor(antigen,levels=c("ashb","sea","msp1","vsp5"))) %>%
  group_by(year,community,antigen) %>%
  arrange(year,community,antigen,age)
dl <- dl %>%
  mutate(logmfi = ifelse(mfi>0,log10(mfi),log10(1)))
# add ROC-based cutoffs
dl$roccut <- NA
dl$roccut[dl$antigen %in% "ashb"] <- log10(418)
dl$roccut[dl$antigen %in% "sea"] <- log10(965)
dl$roccut[dl$antigen %in% "msp1"] <- log10(170)
dl$roccut[dl$antigen %in% "vsp5"] <- log10(281)
# define seroprevalence based on ROC cutoffs
dl <- dl %>%
  mutate(seropos = ifelse(logmfi>roccut,1,0))
# create formatted antigen labels
dl <- dl %>%
  mutate(antigenf=factor(antigen,levels=c("ashb","sea","msp1","vsp5"),
                         labels=c("Ascaris spp. AsHb",
                                  "S. mansoni SEA",
                                  "P. falciparum MSP-1",
                                  "Giardia sp. VSP-5")))
# merge in the KK measurements for Schisto and Ascaris
dl <- left_join(dl,dlkk,by=c("year","community","pid","antigen"))

Age stratified distributions

table(dl$agecat,dl$antigenf)
         
          Ascaris spp. AsHb S. mansoni SEA P. falciparum MSP-1 Giardia sp. VSP-5
  <1 year                51             51                  51                51
  1 year                574            574                 570               570
  2 years               782            782                 780               780
  3 years               900            900                 890               890
  4 years              1378           1378                1372              1372
pdist <- ggplot(data=dl,aes(x=logmfi,fill=antigen)) +
  facet_grid(agecat~antigenf) +
  geom_density(alpha=0.5,color=NA) +
  geom_vline(aes(xintercept = roccut),lty=1) +
  scale_fill_manual(values=pcols)+
  # geom_vline(aes(xintercept = mixcut),lty=2) +
  labs(x="log10 luminex response (MFI-bg)") +
  theme_minimal() + 
  theme(legend.position = "none",
        strip.text.y=element_text(angle=0))
pdist

# limit to just SEA and Giardia for presentation
dlp <- dl %>% filter(antigen %in% c("ashb","sea","vsp5") ) %>%
  mutate(antigenf=factor(antigenf,levels=c("S. mansoni SEA","Giardia sp. VSP-5","Ascaris spp. AsHb")))
pdist2 <- ggplot(data=dlp,aes(x=logmfi,fill=antigen)) +
  facet_grid(agecat~antigenf) +
  geom_density(alpha=0.75,color=NA) +
  geom_vline(aes(xintercept = roccut),lty=1) +
  scale_fill_manual(values=pcols[c(1,2,4)])+
  # geom_vline(aes(xintercept = mixcut),lty=2) +
  labs(x="log10 luminex response (MFI-bg)") +
  theme_minimal() + 
  theme(legend.position = "none",
        strip.text.y=element_text(angle=0,size=14),
        strip.text.x=element_text(size=14))
pdist2
# save png file for presentation
ggsave(filename=here("output","mbita-ab-dists-ashb-sea-vsp.png"),plot=pdist2,device="png",width=8,height=7)

pdist3 <- ggplot(data=dl,aes(x=logmfi,fill=antigenf)) +
  facet_grid(.~antigenf) +
  geom_density(alpha=0.5,color=NA) +
  geom_vline(aes(xintercept = roccut),lty=1) +
  scale_fill_manual(values=pcols)+
  # geom_vline(aes(xintercept = mixcut),lty=2) +
  labs(x="log10 luminex response (MFI-bg)") +
  theme_minimal() + 
  theme(legend.position = "none",
        strip.text.y=element_text(angle=0))
pdist3
# save png file for presentation
ggsave(filename=here("output","mbita-ab-dists.png"),plot=pdist3,device="png",width=8,height=2)

Age dependent means and seroprevalence

Function for a simultaneous CI around a spline curve

#----------------------------------
# simulataneous CIs for GAMs
# estimated by resampling the 
# Baysian posterior estimates of
# the variance-covariance matrix
# assuming that it is multivariate normal
# the function below also estimates 
# the unconditional variance-covariance
# matrix, Vb=vcov(x,unconditional=TRUE), 
# which allows for undertainty in the actual
# estimated mean as well 
# (Marra & Wood 2012 Scandinavian Journal of Statistics, 
#  Vol. 39: 53–74, 2012, doi: 10.1111/j.1467-9469.2011.00760.x )
# simultaneous CIs provide much better coverage than pointwise CIs
# see: http://www.fromthebottomoftheheap.net/2016/12/15/simultaneous-interval-revisited/
#----------------------------------
gamCI <- function(m,newdata,nreps=10000) {
  require(mgcv)
  require(dplyr)
  Vb <- vcov(m,unconditional = TRUE)
  pred <- predict(m, newdata, se.fit = TRUE)
  fit <- pred$fit
  se.fit <- pred$se.fit
  BUdiff <- MASS::mvrnorm(n=nreps, mu = rep(0, nrow(Vb)), Sigma = Vb)
  Cg <- predict(m, newdata, type = "lpmatrix")
  simDev <- Cg %*% t(BUdiff)
  absDev <- abs(sweep(simDev, 1, se.fit, FUN = "/"))
  masd <- apply(absDev, 2L, max)
  crit <- quantile(masd, prob = 0.95, type = 8)
  pred <- data.frame(newdata,fit=pred$fit,se.fit=pred$se.fit)
  pred <- mutate(pred,
                 uprP = fit + (2 * se.fit),
                 lwrP = fit - (2 * se.fit),
                 uprS = fit + (crit * se.fit),
                 lwrS = fit - (crit * se.fit)
  )
  return(pred)
}

Select smoothing parameters with CV

#----------------------------------
# prep data for spline fits
#----------------------------------
dl <- dl %>% 
  ungroup() %>%
  mutate(community=factor(community),
         dummy=1)
#----------------------------------
# choose the smoothing parameter
# for the splines, k, 
# based on cross-validated MSE
# pick smallest k where CV MSE is 
# close to its minimum
#----------------------------------
# library(SuperLearner)
# library(mgcv)
# source(here("R","SL.mgcv.R")) # mgcv wrapper for SuperLearner
# # k=4
# set.seed(123)
# sld <- filter(dl,antigen=="msp1")
# cv_msp1 <- SuperLearner(Y=sld$logmfi,X=select(sld,age), SL.library = paste("SL.mgcv.k",4:10,sep=""))
# cv_msp1
# # k=4
# set.seed(123)
# sld <- filter(dl,antigen=="vsp5")
# cv_vsp5 <- SuperLearner(Y=sld$logmfi,X=select(sld,age), SL.library = paste("SL.mgcv.k",4:10,sep=""))
# cv_vsp5
# # k=4
# set.seed(123)
# sld <- filter(dl,antigen=="ashb")
# cv_ashb <- SuperLearner(Y=sld$logmfi,X=select(sld,age), SL.library = paste("SL.mgcv.k",4:10,sep=""))
# cv_ashb
# # k=4
# set.seed(123)
# sld <- filter(dl,antigen=="sea")
# cv_sea <- SuperLearner(Y=sld$logmfi,X=select(sld,age), SL.library = paste("SL.mgcv.k",4:10,sep=""))
# cv_sea

Age dependent means

#----------------------------------
# fit GAM with a spline for age
# include a random effect for cluster
# estimate simultaneous CIs around the curve
# for the prediction data, set the dummy to 0 to 
# zero out all of the random effects
# see posts on Stack Exchange for explanation:
# https://stats.stackexchange.com/questions/131106/predicting-with-random-effects-in-mgcv-gam/131116#131116
# https://stats.stackexchange.com/questions/189384/predicting-mean-smooth-in-gam-with-smooth-by-random-factor-interaction
#----------------------------------
# Pf Malaria MSP1 
fit_msp1 <- mgcv::gam(logmfi~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="msp1"))
newd <- dl %>% filter(antigen=="msp1") %>% mutate(dummy=0)
fit_msp1ci <- gamCI(m=fit_msp1,newdata=newd,nreps=10000)
# Giardia VSP5
fit_vsp5 <- mgcv::gam(logmfi~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="vsp5"))
newd <- dl %>% filter(antigen=="vsp5") %>% mutate(dummy=0)
fit_vsp5ci <- gamCI(m=fit_vsp5,newdata=newd,nreps=10000)
# Ascaris AsHb
fit_ashb <- mgcv::gam(logmfi~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="ashb"))
newd <- dl %>% filter(antigen=="ashb") %>% mutate(dummy=0)
fit_ashbci <- gamCI(m=fit_ashb,newdata=newd,nreps=10000)
# Schisto SEA
fit_sea <- mgcv::gam(logmfi~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="sea"))
newd <- dl %>% filter(antigen=="sea") %>% mutate(dummy=0)
fit_seaci <- gamCI(m=fit_sea,newdata=newd,nreps=10000)
fit_mfi <- bind_rows(fit_msp1ci,fit_vsp5ci,fit_ashbci,fit_seaci)
pagemfi <- ggplot(data=fit_mfi,aes(x=age,y=fit,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_point(aes(x=age,y=logmfi),alpha=0.1,size=0.2)+
  geom_ribbon(aes(ymin=lwrS,ymax=uprS),alpha=0.2,color=NA,fill="black") +
  geom_line(lwd=0.5,alpha=0.5,color="black") +
  geom_smooth(method="loess",se=FALSE,lwd=0.5,color="black") +
  scale_color_manual(values=pcols)+
  scale_y_continuous(breaks=seq(0,4,by=1))+
  labs(x="age, years",y="log10 Luminex Response (MFI-bg)") +
  theme_minimal() +
  theme(legend.position="none")
pagemfi

Age dependent seroprevalence

# Ascaris AsHb
fitp_ashb <- mgcv::gam(seropos~s(age,bs="cr",k=11) + s(community,bs="re",by=dummy), family="binomial",
                      data=filter(dl,antigen=="ashb"))
newd <- dl %>% filter(antigen=="ashb") %>% mutate(dummy=0)
fitp_ashbci <- gamCI(m=fitp_ashb,newdata=newd,nreps=10000)
# Schisto SEA
fitp_sea <- mgcv::gam(seropos~s(age,bs="cr",k=9) + s(community,bs="re",by=dummy), family="binomial", 
                      data=filter(dl,antigen=="sea"))
newd <- dl %>% filter(antigen=="sea") %>% mutate(dummy=0)
fitp_seaci <- gamCI(m=fitp_sea,newdata=newd,nreps=10000)
# Pf Malaria MSP1 
fitp_msp1 <- mgcv::gam(seropos~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="msp1"))
newd <- dl %>% filter(antigen=="msp1") %>% mutate(dummy=0)
fitp_msp1ci <- gamCI(m=fitp_msp1,newdata=newd,nreps=10000)
# Giardia VSP5
fitp_vsp5 <- mgcv::gam(seropos~s(age,bs="cr",k=4) + s(community,bs="re",by=dummy), 
                      data=filter(dl,antigen=="vsp5"))
newd <- dl %>% filter(antigen=="vsp5") %>% mutate(dummy=0)
fitp_vsp5ci <- gamCI(m=fitp_vsp5,newdata=newd,nreps=10000)
fit_seroprev <- bind_rows(fitp_ashbci,fitp_seaci,fitp_msp1ci,fitp_vsp5ci)
# convert linear predictor to prevalance
expitfn <- function(x) {
  exp(x)/(1+exp(x))
}
fit_seroprev <- fit_seroprev %>%
  mutate(fit = expitfn(fit),
         uprP = expitfn(uprP),
         lwrP = expitfn(lwrP),
         uprS = expitfn(uprS),
         lwrS = expitfn(lwrS),
         )
pageprev <- ggplot(data=fit_seroprev,aes(x=age,y=fit,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_ribbon(aes(ymin=lwrS,ymax=uprS),alpha=0.2,color=NA,fill="grey50") +
  geom_line(lwd=0.5,alpha=0.5) +
  geom_smooth(method="loess",se=FALSE,lwd=0.5) +
  scale_color_manual(values=pcols)+
  scale_y_continuous(breaks=seq(0,0.8,by=0.1),labels=seq(0,80,by=10))+
  labs(x="age, years",y="Seroprevalence (%)") +
  theme_minimal() +
  theme(legend.position="none")
pageprev

Cluster level seroprevalence vs. means

dlc <- dl %>%
  group_by(community,antigenf) %>%
  mutate(posroc=ifelse(logmfi>roccut,1,0)) %>%
  summarize(n=n(),
            meanmfi=mean(logmfi),
            prevroc=mean(posroc))
# estimate pearson correlation
dcorr <- dlc %>%
  ungroup() %>%
  group_by(antigenf) %>%
  mutate(corroc=cor(meanmfi,prevroc,method="pearson")) %>%
  slice(1)
# estimate regression slope
foreach(ai=levels(dlc$antigenf)) %do% {
  di <- dlc %>% filter(antigenf==ai)
  fit <- lm(prevroc~meanmfi,data=di)
  summary(fit)
}
[[1]]

Call:
lm(formula = prevroc ~ meanmfi, data = di)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.058248 -0.018387 -0.003961  0.020299  0.081329 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.38897    0.08000  -4.862 4.05e-05 ***
meanmfi      0.27861    0.04373   6.371 6.79e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.03368 on 28 degrees of freedom
Multiple R-squared:  0.5917,    Adjusted R-squared:  0.5772 
F-statistic: 40.58 on 1 and 28 DF,  p-value: 6.791e-07


[[2]]

Call:
lm(formula = prevroc ~ meanmfi, data = di)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.029840 -0.007884 -0.003631  0.010660  0.024363 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.836794   0.012860  -65.07   <2e-16 ***
meanmfi      0.418625   0.004177  100.22   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01353 on 28 degrees of freedom
Multiple R-squared:  0.9972,    Adjusted R-squared:  0.9971 
F-statistic: 1.004e+04 on 1 and 28 DF,  p-value: < 2.2e-16


[[3]]

Call:
lm(formula = prevroc ~ meanmfi, data = di)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.047709 -0.014638  0.002135  0.013209  0.059840 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.34294    0.05604  -6.119 1.33e-06 ***
meanmfi      0.33966    0.01754  19.362  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.02506 on 28 degrees of freedom
Multiple R-squared:  0.9305,    Adjusted R-squared:  0.928 
F-statistic: 374.9 on 1 and 28 DF,  p-value: < 2.2e-16


[[4]]

Call:
lm(formula = prevroc ~ meanmfi, data = di)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.055515 -0.012084 -0.001454  0.010680  0.048189 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.38331    0.08568  -4.474 0.000117 ***
meanmfi      0.36491    0.03142  11.613 3.22e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.02448 on 28 degrees of freedom
Multiple R-squared:  0.8281,    Adjusted R-squared:  0.8219 
F-statistic: 134.9 on 1 and 28 DF,  p-value: 3.217e-12
pmfivprev <- ggplot(data=dlc,aes(x=meanmfi,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_point(aes(y=prevroc),alpha=0.7) +
  geom_smooth(aes(y=prevroc),method="glm",color="gray40",lwd=0.5,se=FALSE) +
  geom_text(data=dcorr,
            aes(x=2.5,y=0.95,label=paste("r ==",sprintf("%1.2f",corroc)) ),
            parse=TRUE,col="grey30") +
  scale_y_continuous(breaks=seq(0,1,by=0.2),labels=seq(0,100,by=20))+
  scale_color_manual(values=pcols)+
  coord_cartesian(ylim=c(0,1),xlim=c(1,4.5)) +
  labs(x="community mean log10 Luminex response (MFI-bg)",y="community seroprevalence (%)") +
  theme_minimal() +
  theme(legend.position="none")
pmfivprev
# save png file for presentation
ggsave(filename=here("output","mbita-mfi-v-seroprev.png"),plot=pmfivprev,device="png",width=8,height=2.5)

Comparison with FOI

Constant rate model

Estimate community-level FOI for pathogens based on the seroconversion rate (incidence among susceptibles). Assume a constant rate model (i.e., average over all ages). If we assume a constant force of infection (\(\lambda\)), equivalent to assuming an exponential model, then it can be shown that a generalized linear model with a complementary log-log link fit with current status, age-prevalence data is equivalent to an exponential proportional hazards model (Jewell and van der Laan 1995).

\(\log - \log(1-P(Y|A,W)) = \log \lambda + \log A + \beta W\)

Moroever, this model is also equivalent to a catalytic, SIR model with a single, constant rate parameter (Hens et al. 2010; Hens et al. 2012).

# foi_ests <- foreach(ai=c("S. mansoni SEA","P. falciparum MSP-1"),.combine=rbind) %:%
foi_ests <- foreach(ai=levels(dl$antigenf),.combine=rbind) %:%
  foreach(comi=levels(dl$community),.combine=rbind) %do% {
  
  di <- dl %>% filter(antigenf==ai & community==comi)
  gfit <- glm(seropos~1,offset=log(age),data=di,family=binomial(link="cloglog"))
  gsum <- summary(gfit)
  lambda <- as.numeric(exp(gfit$coefficients))
  log_lambda_se  <- sqrt(gsum$cov.unscaled)
  lambda_lb <- as.numeric(exp(gfit$coefficients - 1.96*log_lambda_se))
  lambda_ub <- as.numeric(exp(gfit$coefficients + 1.96*log_lambda_se))
  res <- data.frame(community=comi,antigenf=ai,lambda,lambda_lb,lambda_ub)
  return(res)
}
foi_ests
    community            antigenf      lambda   lambda_lb  lambda_ub
1           1   Ascaris spp. AsHb 0.041863782 0.025274054 0.06934290
2           2   Ascaris spp. AsHb 0.042339484 0.024545840 0.07303201
3           3   Ascaris spp. AsHb 0.029149807 0.018368933 0.04625806
4           4   Ascaris spp. AsHb 0.036752516 0.021728011 0.06216618
5           5   Ascaris spp. AsHb 0.022773389 0.011820450 0.04387542
6           6   Ascaris spp. AsHb 0.017998513 0.008994449 0.03601626
7           7   Ascaris spp. AsHb 0.022051622 0.012532810 0.03880008
8           8   Ascaris spp. AsHb 0.033765694 0.021516341 0.05298866
9           9   Ascaris spp. AsHb 0.039616757 0.024617738 0.06375433
10         10   Ascaris spp. AsHb 0.018560518 0.009289544 0.03708394
11         11   Ascaris spp. AsHb 0.036674649 0.023654735 0.05686091
12         12   Ascaris spp. AsHb 0.041096701 0.027067791 0.06239662
13         13   Ascaris spp. AsHb 0.058682356 0.040939748 0.08411432
14         14   Ascaris spp. AsHb 0.047651488 0.030653857 0.07407434
15         15   Ascaris spp. AsHb 0.014752727 0.006151003 0.03538333
16         16   Ascaris spp. AsHb 0.008838747 0.002216440 0.03524726
17         17   Ascaris spp. AsHb 0.020295125 0.008453613 0.04872379
18         18   Ascaris spp. AsHb 0.038267451 0.025642409 0.05710843
19         19   Ascaris spp. AsHb 0.047696957 0.029981772 0.07587943
20         20   Ascaris spp. AsHb 0.037242287 0.018628487 0.07445521
21         21   Ascaris spp. AsHb 0.019827571 0.007457970 0.05271308
22         22   Ascaris spp. AsHb 0.030912610 0.017539299 0.05448276
23         23   Ascaris spp. AsHb 0.081532026 0.048140679 0.13808429
24         24   Ascaris spp. AsHb 0.055838704 0.034722214 0.08979730
25         25   Ascaris spp. AsHb 0.032687260 0.020004998 0.05340950
26         26   Ascaris spp. AsHb 0.028479070 0.017461961 0.04644710
27         27   Ascaris spp. AsHb 0.074941167 0.046955065 0.11960751
28         28   Ascaris spp. AsHb 0.069144354 0.042286493 0.11306073
29         29   Ascaris spp. AsHb 0.042451297 0.025162029 0.07162032
30         30   Ascaris spp. AsHb 0.055777296 0.030801016 0.10100663
31          1      S. mansoni SEA 0.301685606 0.235967892 0.38570589
32          2      S. mansoni SEA 0.280982899 0.214648648 0.36781685
33          3      S. mansoni SEA 0.626139365 0.518527498 0.75608431
34          4      S. mansoni SEA 0.419230259 0.331890906 0.52955356
35          5      S. mansoni SEA 0.434664442 0.344429588 0.54853933
36          6      S. mansoni SEA 0.303684968 0.242314034 0.38059933
37          7      S. mansoni SEA 0.367399794 0.301418740 0.44782421
38          8      S. mansoni SEA 0.559105709 0.463354347 0.67464392
39          9      S. mansoni SEA 0.411512188 0.332893387 0.50869824
40         10      S. mansoni SEA 0.417250184 0.334439865 0.52056508
41         11      S. mansoni SEA 0.558480853 0.460506003 0.67730032
42         12      S. mansoni SEA 0.212817923 0.170920060 0.26498626
43         13      S. mansoni SEA 0.196508610 0.156703863 0.24642426
44         14      S. mansoni SEA 0.264851368 0.209457494 0.33489490
45         15      S. mansoni SEA 0.103910754 0.072629862 0.14866399
46         16      S. mansoni SEA 0.150251972 0.101818298 0.22172493
47         17      S. mansoni SEA 0.042409429 0.022845562 0.07872687
48         18      S. mansoni SEA 0.153709691 0.122821980 0.19236515
49         19      S. mansoni SEA 0.053934849 0.034757328 0.08369366
50         20      S. mansoni SEA 0.032242683 0.015374053 0.06761981
51         21      S. mansoni SEA 0.110300440 0.070220980 0.17325573
52         22      S. mansoni SEA 0.096913615 0.068777839 0.13655923
53         23      S. mansoni SEA 0.081113951 0.047839210 0.13753306
54         24      S. mansoni SEA 0.028012170 0.014581699 0.05381278
55         25      S. mansoni SEA 0.015847599 0.007920106 0.03170998
56         26      S. mansoni SEA 0.196810250 0.157649801 0.24569821
57         27      S. mansoni SEA 0.103958145 0.069101500 0.15639741
58         28      S. mansoni SEA 0.032379144 0.016190025 0.06475648
59         29      S. mansoni SEA 0.042517154 0.025210485 0.07170463
60         30      S. mansoni SEA 0.045251259 0.023560651 0.08691086
61          1 P. falciparum MSP-1 0.407637151 0.320078189 0.51914830
62          2 P. falciparum MSP-1 0.427737126 0.329406474 0.55542032
63          3 P. falciparum MSP-1 0.490430794 0.408358958 0.58899740
64          4 P. falciparum MSP-1 0.497440179 0.393612041 0.62865641
65          5 P. falciparum MSP-1 0.368167187 0.291158296 0.46554427
66          6 P. falciparum MSP-1 0.457272614 0.367294294 0.56929347
67          7 P. falciparum MSP-1 0.362770359 0.297544349 0.44229485
68          8 P. falciparum MSP-1 0.262863661 0.214847856 0.32161040
69          9 P. falciparum MSP-1 0.219009632 0.172317206 0.27835420
70         10 P. falciparum MSP-1 0.321838684 0.256492308 0.40383331
71         11 P. falciparum MSP-1 0.501949931 0.414488378 0.60786682
72         12 P. falciparum MSP-1 0.462092754 0.379376546 0.56284374
73         13 P. falciparum MSP-1 0.588934359 0.481939700 0.71968273
74         14 P. falciparum MSP-1 0.574104330 0.458162656 0.71938596
75         15 P. falciparum MSP-1 0.518413852 0.406380970 0.66133245
76         16 P. falciparum MSP-1 0.286884906 0.206676892 0.39822037
77         17 P. falciparum MSP-1 0.310148268 0.229968269 0.41828357
78         18 P. falciparum MSP-1 0.562744126 0.468443729 0.67602773
79         19 P. falciparum MSP-1 0.508307213 0.401108999 0.64415464
80         20 P. falciparum MSP-1 0.342897988 0.248084342 0.47394781
81         21 P. falciparum MSP-1 0.373106782 0.269934993 0.51571184
82         22 P. falciparum MSP-1 0.503276492 0.395398068 0.64058792
83         23 P. falciparum MSP-1 0.297799158 0.211163176 0.41998013
84         24 P. falciparum MSP-1 0.562119613 0.434555864 0.72712966
85         25 P. falciparum MSP-1 0.546408800 0.443612365 0.67302582
86         26 P. falciparum MSP-1 0.483602893 0.397036708 0.58904316
87         27 P. falciparum MSP-1 0.349169873 0.261968348 0.46539821
88         28 P. falciparum MSP-1 0.593993450 0.441420952 0.79930102
89         29 P. falciparum MSP-1 0.652295400 0.507769041 0.83795831
90         30 P. falciparum MSP-1 0.449384128 0.328414573 0.61491210
91          1   Giardia sp. VSP-5 0.303039279 0.235896095 0.38929345
92          2   Giardia sp. VSP-5 0.221509436 0.165513907 0.29644899
93          3   Giardia sp. VSP-5 0.315019539 0.260903835 0.38035972
94          4   Giardia sp. VSP-5 0.367568707 0.290423541 0.46520593
95          5   Giardia sp. VSP-5 0.365506132 0.289008626 0.46225171
96          6   Giardia sp. VSP-5 0.271173583 0.214900364 0.34218235
97          7   Giardia sp. VSP-5 0.314953175 0.257351293 0.38544785
98          8   Giardia sp. VSP-5 0.360752935 0.298256395 0.43634498
99          9   Giardia sp. VSP-5 0.372873268 0.300987583 0.46192761
100        10   Giardia sp. VSP-5 0.263988144 0.208595594 0.33409018
101        11   Giardia sp. VSP-5 0.232172528 0.187891999 0.28688865
102        12   Giardia sp. VSP-5 0.222601106 0.178955009 0.27689224
103        13   Giardia sp. VSP-5 0.289231163 0.235426303 0.35533271
104        14   Giardia sp. VSP-5 0.297846696 0.236794953 0.37463913
105        15   Giardia sp. VSP-5 0.263812414 0.202842651 0.34310826
106        16   Giardia sp. VSP-5 0.257431919 0.184190485 0.35979705
107        17   Giardia sp. VSP-5 0.243361928 0.177584234 0.33350386
108        18   Giardia sp. VSP-5 0.269187861 0.222283620 0.32598940
109        19   Giardia sp. VSP-5 0.271835038 0.212303320 0.34805997
110        20   Giardia sp. VSP-5 0.199340177 0.139334279 0.28518830
111        21   Giardia sp. VSP-5 0.319205568 0.229272920 0.44441443
112        22   Giardia sp. VSP-5 0.290699506 0.226858556 0.37250613
113        23   Giardia sp. VSP-5 0.258931634 0.181754776 0.36887939
114        24   Giardia sp. VSP-5 0.262222117 0.200019141 0.34376929
115        25   Giardia sp. VSP-5 0.223646968 0.177972964 0.28104250
116        26   Giardia sp. VSP-5 0.265771586 0.216026888 0.32697104
117        27   Giardia sp. VSP-5 0.359869156 0.270273538 0.47916570
118        28   Giardia sp. VSP-5 0.357305232 0.266570015 0.47892494
119        29   Giardia sp. VSP-5 0.306640041 0.237398847 0.39607654
120        30   Giardia sp. VSP-5 0.328328900 0.237820724 0.45328205
# merge FOI estimates to the cluster-level means
d_foi <- dlc %>%
  # filter(antigenf=="S. mansoni SEA"|antigenf=="P. falciparum MSP-1") %>%
  left_join(foi_ests,by=c("community","antigenf"))# %>%
  # mutate(antigenf=factor(antigenf,levels=c("S. mansoni SEA","P. falciparum MSP-1")))
# estimate spearman correlation
dfoicorr <- d_foi %>%
  ungroup() %>%
  group_by(antigenf) %>%
  mutate(cormfi=cor(meanmfi,lambda,method="spearman"),
         corprev=cor(prevroc,lambda,method="spearman")) %>%
  slice(1)

Figure of results

pfoivmfi <- ggplot(data=d_foi,aes(x=meanmfi,y=lambda,color=antigenf)) +
  facet_grid(~antigenf)+
  geom_point(,alpha=0.7) +
  # geom_smooth(method="glm",color="gray40",lwd=0.5,se=FALSE) +
  geom_smooth(method="loess",color="gray40",lwd=0.5,se=FALSE) +
  geom_text(data=dfoicorr,
            aes(x=2.5,y=0.6,label=paste("rho ==",sprintf("%1.2f",cormfi)) ),
            parse=TRUE,col="grey30") +
  scale_x_continuous(breaks=2:4)+
  scale_color_manual(values=pcols)+
  coord_cartesian(xlim=c(1.5,4),ylim=c(0,0.7)) +
  labs(x="community mean log10 Luminex response (MFI-bg)",y=expression(paste("community seroconversion rate (",lambda,")"))) +
  theme_minimal() +
  theme(legend.position="none")
pfoivmfi
# save png file for presentation
ggsave(filename=here("output","mbita-mfi-v-foi.png"),plot=pfoivmfi,device="png",width=8,height=2.5)

pfoivprev <- ggplot(data=d_foi,aes(x=prevroc,y=lambda,color=antigenf)) +
  facet_grid(~antigenf)+
  geom_point(alpha=0.7) +
  # geom_smooth(method="glm",color="gray40",lwd=0.5,se=FALSE) +
  geom_smooth(method="loess",color="gray40",lwd=0.5,se=FALSE) +
  geom_text(data=dfoicorr,
            aes(x=0.2,y=0.6,label=paste("rho ==",sprintf("%1.2f",corprev)) ),
            parse=TRUE,col="grey30") +
  scale_x_continuous(breaks=seq(0,1,by=0.2),labels=seq(0,100,by=20))+
  scale_color_manual(values=pcols)+
  coord_cartesian(xlim=c(0,1),ylim=c(0,0.7)) +
  labs(x="community seroprevalence (%)",y=expression(paste("community seroconversion rate (",lambda,")"))) +
  theme_minimal() +
  theme(legend.position="none")
pfoivprev
# save png file for presentation
ggsave(filename=here("output","mbita-prev-v-foi.png"),plot=pfoivprev,device="png",width=8,height=3)

Comparison with Kato-Katz

dlckk <- dl %>%
  group_by(community,antigenf) %>%
  filter(antigen %in% c("ashb","sea")) %>%
  mutate(posroc=ifelse(logmfi>roccut,1,0),
         logkk=ifelse(epg>0,log10(epg),log10(1)),
         poskk=ifelse(epg>0,1,0)) %>%
  summarize(n=n(),
            meanmfi=mean(logmfi),
            prevroc=mean(posroc),
            meankk=mean(logkk,na.rm=T),
            prevkk=mean(poskk,na.rm=T))
# estimate pearson correlation
dcorrkk <- dlckk %>%
  ungroup() %>%
  group_by(antigenf) %>%
  mutate(corspkk=cor(prevroc,prevkk,method="spearman"),
         cormukk=cor(meanmfi,prevkk,method="spearman"),
         cormukkmu=cor(meanmfi,meankk,method="spearman")) %>%
  slice(1)

Figure of results

# KK prevalence versus MFI
pmfivkk <- ggplot(data=dlckk,aes(x=meanmfi,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_point(aes(y=prevkk),alpha=0.7) +
  geom_smooth(aes(y=prevkk),method="loess",color="gray40",lwd=0.5,se=FALSE) +
  geom_text(data=dcorrkk,
            aes(x=1,y=0.55,label=paste("rho ==",sprintf("%1.2f",cormukk)) ),
            parse=TRUE,col="grey30",hjust=0) +
  scale_y_continuous(breaks=seq(0,0.6,by=0.2),labels=seq(0,60,by=20))+
  scale_color_manual(values=pcols)+
  coord_cartesian(ylim=c(0,0.6),xlim=c(1,4.5)) +
  labs(x="community mean log10 Luminex response",y="community prevalence by Kato-Katz (%)") +
  theme_minimal() +
  theme(legend.position="none")
pmfivkk
# save png file for presentation
ggsave(filename=here("output","mbita-mfi-v-kkprev.png"),plot=pmfivkk,device="png",width=5,height=3)

# KK mean versus MFI
pmfivkkmean <- ggplot(data=dlckk,aes(x=meanmfi,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_point(aes(y=meankk),alpha=0.7) +
  geom_smooth(aes(y=meankk),method="loess",color="gray40",lwd=0.5,se=FALSE) +
  geom_text(data=dcorrkk,
            aes(x=1,y=1,label=paste("rho ==",sprintf("%1.2f",cormukkmu)) ),
            parse=TRUE,col="grey30",hjust=0) +
  scale_y_continuous(breaks=seq(0,1,by=0.2))+
  scale_color_manual(values=pcols)+
  coord_cartesian(ylim=c(0,1.1),xlim=c(1,4.5)) +
  labs(x="community mean log10 Luminex response",y="community mean log10 eggs per gram") +
  theme_minimal() +
  theme(legend.position="none")
pmfivkkmean
# save png file for presentation
ggsave(filename=here("output","mbita-mfi-v-kkmean.png"),plot=pmfivkkmean,device="png",width=5,height=3)

# KK prevalence versus seroprevalence
pspvkk <- ggplot(data=dlckk,aes(x=prevroc,color=antigenf)) +
  facet_grid(~antigenf) +
  geom_point(aes(y=prevkk),alpha=0.7) +
  geom_smooth(aes(y=prevkk),method="loess",color="gray40",lwd=0.5,se=FALSE) +
  geom_abline(intercept=0,slope=1,lty="dashed",color="gray40",lwd=0.25)+
  geom_text(data=dcorrkk,
            aes(x=0.1,y=0.8,label=paste("rho ==",sprintf("%1.2f",corspkk)) ),
            parse=TRUE,col="grey30",hjust=0) +
  scale_y_continuous(breaks=seq(0,0.8,by=0.2),labels=seq(0,80,by=20))+
  scale_x_continuous(breaks=seq(0,0.8,by=0.2),labels=seq(0,80,by=20))+
  scale_color_manual(values=pcols)+
  coord_cartesian(ylim=c(0,0.9),xlim=c(0,0.9)) +
  labs(x="community seroprevalence (%)",y="community prevalence by Kato-Katz (%)") +
  theme_minimal() +
  theme(legend.position="none")
pspvkk
# save png file for presentation
ggsave(filename=here("output","mbita-serop-v-kkprev.png"),plot=pspvkk,device="png",width=5,height=3)

Effect of smaller samples

Downsample the observations within each community to see how that influences the relationships

Note: this analysis probably won’t contribute to the presentation because not sufficient time to think about it and interpret it.

# estimate means with sample sizes of betweeen 10 and 50 observations
set.seed(123)
ssests <- foreach(ssi=seq(100,10,by=-10),.combine=rbind) %:% 
  foreach(iteri=1:1000,.combine=rbind) %dopar% {
    di <- dl %>%
    group_by(community,antigenf) %>%
    sample_n(size=ssi,replace=TRUE) %>%
    summarize(meanmfi=mean(logmfi),
              seroprev=mean(seropos)) %>%
    mutate(ss=ssi,iter=iteri)
}
# merge in the means with the full sample
dlc2 <- dlc %>%
  select(community,antigenf,n,mean=meanmfi,serop=prevroc) %>%
  left_join(select(d_foi,community,antigenf,lambda),by=c("community","antigenf")) %>%
  mutate(antigenf=factor(antigenf,levels=levels(dl$antigenf)))
ssests2 <- left_join(ssests,dlc2,by=c("community","antigenf"))
# average over bootstrap estimates
ssmeans <- ssests2 %>%
  group_by(community,antigenf,ss) %>%
  summarize(mean=mean(mean),
            mu=mean(meanmfi),
            mu_sd=sd(meanmfi),
            mu_lb=quantile(meanmfi,probs=c(0.025)),
            mu_ub=quantile(meanmfi,probs=c(0.975)),
            mu_mse=mean((meanmfi-mean)^2),
            mu_bias=mean(meanmfi-mean),
            
            serop=mean(serop),
            prev=mean(seroprev),
            prev_sd=sd(seroprev),
            prev_lb=quantile(seroprev,probs=c(0.025)),
            prev_ub=quantile(seroprev,probs=c(0.975)),
            prev_mse=mean((seroprev-serop)^2),
            prev_bias=mean(seroprev-serop)
            )
# correlation betwen community-level means and seroprev, over bootstrap estimates
sscorr <- ssests2 %>%
  group_by(antigenf,ss,iter) %>%
  summarize(cor_mean=cor(meanmfi,serop,method="pearson"),
            cor_prev=cor(seroprev,serop,method="pearson")) %>%
  ungroup() %>%
  group_by(antigenf,ss) %>%
  summarize(cor_mu=mean(cor_mean),
            cor_mu_lb=quantile(cor_mean,probs=c(0.025)),
            cor_mu_ub=quantile(cor_mean,probs=c(0.975)),
            cor_pr=mean(cor_prev),
            cor_pr_lb=quantile(cor_prev,probs=c(0.025)),
            cor_pr_ub=quantile(cor_prev,probs=c(0.975)),
            )
# correlation betwen community-level means and seroprev, over bootstrap estimates
sscorr_foi <- ssests2 %>%
  group_by(antigenf,ss,iter) %>%
  filter(!is.na(lambda)) %>%
  summarize(cor_mean=cor(meanmfi,lambda,method="spearman"),
            cor_prev=cor(seroprev,lambda,method="spearman")) %>%
  ungroup() %>%
  group_by(antigenf,ss) %>%
  summarize(cor_mu=mean(cor_mean),
            cor_mu_lb=quantile(cor_mean,probs=c(0.025)),
            cor_mu_ub=quantile(cor_mean,probs=c(0.975)),
            cor_pr=mean(cor_prev),
            cor_pr_lb=quantile(cor_prev,probs=c(0.025)),
            cor_pr_ub=quantile(cor_prev,probs=c(0.975)),
            )

Plot a figure of RMSE in community-level estimates by sample size.

ssmumsep <- ggplot(data=ssmeans,aes(x=ss,y=sqrt(mu_mse),group=community)) +
  facet_grid(.~antigenf)+
  geom_line(alpha=0.2) +
  theme_minimal()
ssmumsep

ssprevmsep <- ggplot(data=ssmeans,aes(x=ss,y=sqrt(prev_mse),group=community)) +
  facet_grid(.~antigenf)+
  geom_line(alpha=0.2) +
  theme_minimal()
ssprevmsep

Plot a figure of the correlation between the seroprevalence estimated in the full sample versus cluster level mean (blue) or cluster level seroprevalence (orange) estimated at each smaller sample size

sscorp <- ggplot(data=sscorr,aes(x=ss)) +
  facet_grid(.~antigenf)+
  geom_ribbon(aes(ymin=cor_mu_lb,ymax=cor_mu_ub),color=NA,fill=cblue,alpha=0.2)+
  geom_line(aes(y=cor_mu),color=cblue) +
  geom_ribbon(aes(ymin=cor_pr_lb,ymax=cor_pr_ub),color=NA,fill=corange,alpha=0.2)+
  geom_line(aes(y=cor_pr),color=corange)+
  coord_cartesian(ylim=c(0,1))+
  labs(y="Correlation with community level seroprevalence",x="Sample size per community") +
  theme_minimal()
sscorp

Plot a figure of the correlation between the seroconversion rate estimated in the full sample versus cluster level mean (blue) or cluster level seroprevalence (orange) estimated at each smaller sample size

sscorfoip <- ggplot(data=sscorr_foi,aes(x=ss)) +
  facet_grid(.~antigenf)+
  geom_ribbon(aes(ymin=cor_mu_lb,ymax=cor_mu_ub),color=NA,fill=cblue,alpha=0.2)+
  geom_line(aes(y=cor_mu),color=cblue) +
  geom_ribbon(aes(ymin=cor_pr_lb,ymax=cor_pr_ub),color=NA,fill=corange,alpha=0.2)+
  geom_line(aes(y=cor_pr),color=corange)+
  coord_cartesian(ylim=c(0,1))+
  labs(y="Correlation with community level seroconversion rate",x="Sample size per community") +
  theme_minimal()
sscorfoip

Based on these figures, there is no loss of information from reducing quantitative measures to seroprevalence, and if anything seroprevalence performs slightly better with respect to correlation measures.

LS0tCnRpdGxlOiAiTWJpdGEgS2VueWEgQWIgc3R1ZHkiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogZGVmYXVsdAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKLS0tCgoKIyBQcmVhbWJsZQpgYGB7ciBwcmVhbWJsZX0KbGlicmFyeShoZXJlKQpoZXJlKCkKCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1nY3YpCmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBzZXQgdXAgZm9yIHBhcmFsbGVsIGNvbXB1dGluZwojIGNvbmZpZ3VyZSBmb3IgYSBsYXB0b3AgKHVzZSBvbmx5IDMgY29yZXMpCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShkb1BhcmFsbGVsKQpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXM9MykKCiMgZ3JhYiBzb21lIGNvbG9ycyBmb3IgcGxvdHRpbmcKZ2dwbG90Y29scyA8LSBzY2FsZXM6Omh1ZV9wYWwoKSgzKQojIGJyaWdodCBjb2xvciBibGluZCBwYWxldHRlOiAgaHR0cHM6Ly9wZXJzb25hbC5zcm9uLm5sL35wYXVsdC8gCmNibGFjayA8LSAiIzAwMDAwNEZGIgpjYmx1ZSA8LSAiIzMzNjZBQSIKY3RlYWwgPC0gIiMxMUFBOTkiCmNncmVlbiA8LSAiIzY2QUE1NSIKY2NoYXJ0ciA8LSAiI0NDQ0M1NSIKY21hZ2VudCA8LSAiIzk5MjI4OCIKY3JlZCA8LSAiI0VFMzMzMyIKY29yYW5nZSA8LSAiI0VFQTcyMiIKY3llbGxvdyA8LSAiI0ZGRUUzMyIKY2dyZXkgPC0gIiM3Nzc3NzciCgpwY29scyA8LSBjKGdncGxvdGNvbHMsY29yYW5nZSkKCmBgYAoKIyBMb2FkIE1iaXRhIEtlbnlhIGFudGlib2R5IG1lYXN1cmVtZW50cwoKTm90ZTogYXQgdGhpcyB0aW1lIHRoZXNlIGRhdGEgYXJlIG5vdCB5ZXQgcHVibGljbHkgYXZhaWxhYmxlLiBUaGV5IGFyZSBmcm9tIHRoaXMgc3R1ZHk6CldvbiBLWSwgS2FueWkgSE0sIE13ZW5kZSBGTSwgV2llZ2FuZCBSRSwgQnJvb2sgR29vZGhldyBFLCBQcmllc3QgSlcsIGV0IGFsLiBNdWx0aXBsZXggU2Vyb2xvZ2ljIEFzc2Vzc21lbnQgb2YgU2NoaXN0b3NvbWlhc2lzIGluIFdlc3Rlcm4gS2VueWE6IEFudGlib2R5IFJlc3BvbnNlcyBpbiBQcmVzY2hvb2wgQWdlIENoaWxkcmVuIGFzIGEgTWVhc3VyZSBvZiBSZWR1Y2VkIFRyYW5zbWlzc2lvbi4gX0FtIEogVHJvcCBNZWQgSHlnLl8gMjAxNzsgMTbigJMwNjY1LiBodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8yODcxOTI4MAoKYGBge3IgbG9hZCBkYXRhLCB3YXJuaW5nPUZBTFNFfQpkIDwtIHJlYWRSRFMoaGVyZSgiZGF0YSIsIm1iaXRhX3BzYWMucmRzIikpCgojIGNyZWF0ZSBhZ2Ugc3RyYXRhCmQgPC0gZCAlPiUKICBzZWxlY3QoeWVhcixjb21tdW5pdHk9dmlkLAogICAgICAgICBwaWQsYWdlPWFnZXksCiAgICAgICAgIGFzY19lcGcsc21fZXBnLAogICAgICAgICBhc2hiLHNlYSxtc3AxLHZzcDUpICU+JQogIG11dGF0ZShhZ2VjYXQgPSBjdXQoYWdlLGJyZWFrcz1jKDAsMSwyLDMsNCw2KSxsYWJlbHM9YygiPDEgeWVhciIsIjEgeWVhciIsIjIgeWVhcnMiLCIzIHllYXJzIiwiNCB5ZWFycyIpKSkKCiMgcmVzaGFwZSBLSyBkYXRhIHRvIGxvbmcgZm9ybWF0CiMgdGhlIGVnZ3MgcGVyIGdyYW0gKEVQRykgbWVhc3VyZXMgYXJlIGdyb3VwZWQgYnkgImFudGlnZW4iIAojIChhIHdyb25nIGxhYmVsKSBqdXN0IHRvIG1ha2UgdGhlbSBlYXN5IHRvIG1lcmdlIGJhY2sgdG8gdGhlIGFudGlib2R5IGRhdGEKZGxrayA8LSBkICU+JQogIHNlbGVjdCh5ZWFyLGNvbW11bml0eSxwaWQsYXNjX2VwZyxzbV9lcGcsKSAlPiUKICBnYXRoZXIoa2V5PWFudGlnZW4sdmFsdWU9ZXBnLC15ZWFyLC1jb21tdW5pdHksLXBpZCkgJT4lCiAgbXV0YXRlKGFudGlnZW4gPSBmYWN0b3IoYW50aWdlbixsZXZlbHM9YygiYXNjX2VwZyIsInNtX2VwZyIpLGxhYmVscz1jKCJhc2hiIiwic2VhIikpKSAlPiUKICBncm91cF9ieSh5ZWFyLGNvbW11bml0eSxhbnRpZ2VuKSAlPiUKICBhcnJhbmdlKHllYXIsY29tbXVuaXR5LGFudGlnZW4pCgojIHJlc2hhcGUgYW50aWJvZHkgZGF0YSB0byBsb25nIGZvcm1hdApkbCA8LSBkICU+JQogIHNlbGVjdCh5ZWFyLGNvbW11bml0eSxwaWQsYWdlLGFnZWNhdCxhc2hiLHNlYSxtc3AxLHZzcDUpICU+JQogIGdhdGhlcihrZXk9YW50aWdlbix2YWx1ZT1tZmksLXllYXIsLWNvbW11bml0eSwtcGlkLC1hZ2UsLWFnZWNhdCkgJT4lCiAgbXV0YXRlKGFudGlnZW4gPSBmYWN0b3IoYW50aWdlbixsZXZlbHM9YygiYXNoYiIsInNlYSIsIm1zcDEiLCJ2c3A1IikpKSAlPiUKICBncm91cF9ieSh5ZWFyLGNvbW11bml0eSxhbnRpZ2VuKSAlPiUKICBhcnJhbmdlKHllYXIsY29tbXVuaXR5LGFudGlnZW4sYWdlKQoKZGwgPC0gZGwgJT4lCiAgbXV0YXRlKGxvZ21maSA9IGlmZWxzZShtZmk+MCxsb2cxMChtZmkpLGxvZzEwKDEpKSkKCiMgYWRkIFJPQy1iYXNlZCBjdXRvZmZzCmRsJHJvY2N1dCA8LSBOQQpkbCRyb2NjdXRbZGwkYW50aWdlbiAlaW4lICJhc2hiIl0gPC0gbG9nMTAoNDE4KQpkbCRyb2NjdXRbZGwkYW50aWdlbiAlaW4lICJzZWEiXSA8LSBsb2cxMCg5NjUpCmRsJHJvY2N1dFtkbCRhbnRpZ2VuICVpbiUgIm1zcDEiXSA8LSBsb2cxMCgxNzApCmRsJHJvY2N1dFtkbCRhbnRpZ2VuICVpbiUgInZzcDUiXSA8LSBsb2cxMCgyODEpCgojIGRlZmluZSBzZXJvcHJldmFsZW5jZSBiYXNlZCBvbiBST0MgY3V0b2ZmcwpkbCA8LSBkbCAlPiUKICBtdXRhdGUoc2Vyb3BvcyA9IGlmZWxzZShsb2dtZmk+cm9jY3V0LDEsMCkpCgoKIyBjcmVhdGUgZm9ybWF0dGVkIGFudGlnZW4gbGFiZWxzCmRsIDwtIGRsICU+JQogIG11dGF0ZShhbnRpZ2VuZj1mYWN0b3IoYW50aWdlbixsZXZlbHM9YygiYXNoYiIsInNlYSIsIm1zcDEiLCJ2c3A1IiksCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQXNjYXJpcyBzcHAuIEFzSGIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlMuIG1hbnNvbmkgU0VBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQLiBmYWxjaXBhcnVtIE1TUC0xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHaWFyZGlhIHNwLiBWU1AtNSIpKSkKCgojIG1lcmdlIGluIHRoZSBLSyBtZWFzdXJlbWVudHMgZm9yIFNjaGlzdG8gYW5kIEFzY2FyaXMKZGwgPC0gbGVmdF9qb2luKGRsLGRsa2ssYnk9YygieWVhciIsImNvbW11bml0eSIsInBpZCIsImFudGlnZW4iKSkKCgpgYGAKCgojIEFnZSBzdHJhdGlmaWVkIGRpc3RyaWJ1dGlvbnMKCmBgYHtyIGRlbnNpdHkgcGxvdCBieSBhZ2V9Cgp0YWJsZShkbCRhZ2VjYXQsZGwkYW50aWdlbmYpCgpwZGlzdCA8LSBnZ3Bsb3QoZGF0YT1kbCxhZXMoeD1sb2dtZmksZmlsbD1hbnRpZ2VuKSkgKwogIGZhY2V0X2dyaWQoYWdlY2F0fmFudGlnZW5mKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSxjb2xvcj1OQSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSByb2NjdXQpLGx0eT0xKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXBjb2xzKSsKICAjIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtaXhjdXQpLGx0eT0yKSArCiAgbGFicyh4PSJsb2cxMCBsdW1pbmV4IHJlc3BvbnNlIChNRkktYmcpIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBzdHJpcC50ZXh0Lnk9ZWxlbWVudF90ZXh0KGFuZ2xlPTApKQoKcGRpc3QKCiMgbGltaXQgdG8ganVzdCBTRUEgYW5kIEdpYXJkaWEgZm9yIHByZXNlbnRhdGlvbgpkbHAgPC0gZGwgJT4lIGZpbHRlcihhbnRpZ2VuICVpbiUgYygiYXNoYiIsInNlYSIsInZzcDUiKSApICU+JQogIG11dGF0ZShhbnRpZ2VuZj1mYWN0b3IoYW50aWdlbmYsbGV2ZWxzPWMoIlMuIG1hbnNvbmkgU0VBIiwiR2lhcmRpYSBzcC4gVlNQLTUiLCJBc2NhcmlzIHNwcC4gQXNIYiIpKSkKcGRpc3QyIDwtIGdncGxvdChkYXRhPWRscCxhZXMoeD1sb2dtZmksZmlsbD1hbnRpZ2VuKSkgKwogIGZhY2V0X2dyaWQoYWdlY2F0fmFudGlnZW5mKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNzUsY29sb3I9TkEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcm9jY3V0KSxsdHk9MSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1wY29sc1tjKDEsMiw0KV0pKwogICMgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1peGN1dCksbHR5PTIpICsKICBsYWJzKHg9ImxvZzEwIGx1bWluZXggcmVzcG9uc2UgKE1GSS1iZykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoYW5nbGU9MCxzaXplPTE0KSwKICAgICAgICBzdHJpcC50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpKQoKcGRpc3QyCiMgc2F2ZSBwbmcgZmlsZSBmb3IgcHJlc2VudGF0aW9uCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCJtYml0YS1hYi1kaXN0cy1hc2hiLXNlYS12c3AucG5nIikscGxvdD1wZGlzdDIsZGV2aWNlPSJwbmciLHdpZHRoPTgsaGVpZ2h0PTcpCgoKCnBkaXN0MyA8LSBnZ3Bsb3QoZGF0YT1kbCxhZXMoeD1sb2dtZmksZmlsbD1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKC5+YW50aWdlbmYpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGNvbG9yPU5BKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IHJvY2N1dCksbHR5PTEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9cGNvbHMpKwogICMgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1peGN1dCksbHR5PTIpICsKICBsYWJzKHg9ImxvZzEwIGx1bWluZXggcmVzcG9uc2UgKE1GSS1iZykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoYW5nbGU9MCkpCgpwZGlzdDMKCiMgc2F2ZSBwbmcgZmlsZSBmb3IgcHJlc2VudGF0aW9uCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCJtYml0YS1hYi1kaXN0cy5wbmciKSxwbG90PXBkaXN0MyxkZXZpY2U9InBuZyIsd2lkdGg9OCxoZWlnaHQ9MikKCmBgYAoKCiMgQWdlIGRlcGVuZGVudCBtZWFucyBhbmQgc2Vyb3ByZXZhbGVuY2UKCiMjIEZ1bmN0aW9uIGZvciBhIHNpbXVsdGFuZW91cyBDSSBhcm91bmQgYSBzcGxpbmUgY3VydmUKYGBge3Igc2ltdWx0YW5lb3VzIENJfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIHNpbXVsYXRhbmVvdXMgQ0lzIGZvciBHQU1zCiMgZXN0aW1hdGVkIGJ5IHJlc2FtcGxpbmcgdGhlIAojIEJheXNpYW4gcG9zdGVyaW9yIGVzdGltYXRlcyBvZgojIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeAojIGFzc3VtaW5nIHRoYXQgaXQgaXMgbXVsdGl2YXJpYXRlIG5vcm1hbAojIHRoZSBmdW5jdGlvbiBiZWxvdyBhbHNvIGVzdGltYXRlcyAKIyB0aGUgdW5jb25kaXRpb25hbCB2YXJpYW5jZS1jb3ZhcmlhbmNlCiMgbWF0cml4LCBWYj12Y292KHgsdW5jb25kaXRpb25hbD1UUlVFKSwgCiMgd2hpY2ggYWxsb3dzIGZvciB1bmRlcnRhaW50eSBpbiB0aGUgYWN0dWFsCiMgZXN0aW1hdGVkIG1lYW4gYXMgd2VsbCAKIyAoTWFycmEgJiBXb29kIDIwMTIgU2NhbmRpbmF2aWFuIEpvdXJuYWwgb2YgU3RhdGlzdGljcywgCiMgIFZvbC4gMzk6IDUz4oCTNzQsIDIwMTIsIGRvaTogMTAuMTExMS9qLjE0NjctOTQ2OS4yMDExLjAwNzYwLnggKQojIHNpbXVsdGFuZW91cyBDSXMgcHJvdmlkZSBtdWNoIGJldHRlciBjb3ZlcmFnZSB0aGFuIHBvaW50d2lzZSBDSXMKIyBzZWU6IGh0dHA6Ly93d3cuZnJvbXRoZWJvdHRvbW9mdGhlaGVhcC5uZXQvMjAxNi8xMi8xNS9zaW11bHRhbmVvdXMtaW50ZXJ2YWwtcmV2aXNpdGVkLwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZ2FtQ0kgPC0gZnVuY3Rpb24obSxuZXdkYXRhLG5yZXBzPTEwMDAwKSB7CiAgcmVxdWlyZShtZ2N2KQogIHJlcXVpcmUoZHBseXIpCiAgVmIgPC0gdmNvdihtLHVuY29uZGl0aW9uYWwgPSBUUlVFKQogIHByZWQgPC0gcHJlZGljdChtLCBuZXdkYXRhLCBzZS5maXQgPSBUUlVFKQogIGZpdCA8LSBwcmVkJGZpdAogIHNlLmZpdCA8LSBwcmVkJHNlLmZpdAogIEJVZGlmZiA8LSBNQVNTOjptdnJub3JtKG49bnJlcHMsIG11ID0gcmVwKDAsIG5yb3coVmIpKSwgU2lnbWEgPSBWYikKICBDZyA8LSBwcmVkaWN0KG0sIG5ld2RhdGEsIHR5cGUgPSAibHBtYXRyaXgiKQogIHNpbURldiA8LSBDZyAlKiUgdChCVWRpZmYpCiAgYWJzRGV2IDwtIGFicyhzd2VlcChzaW1EZXYsIDEsIHNlLmZpdCwgRlVOID0gIi8iKSkKICBtYXNkIDwtIGFwcGx5KGFic0RldiwgMkwsIG1heCkKICBjcml0IDwtIHF1YW50aWxlKG1hc2QsIHByb2IgPSAwLjk1LCB0eXBlID0gOCkKICBwcmVkIDwtIGRhdGEuZnJhbWUobmV3ZGF0YSxmaXQ9cHJlZCRmaXQsc2UuZml0PXByZWQkc2UuZml0KQogIHByZWQgPC0gbXV0YXRlKHByZWQsCiAgICAgICAgICAgICAgICAgdXByUCA9IGZpdCArICgyICogc2UuZml0KSwKICAgICAgICAgICAgICAgICBsd3JQID0gZml0IC0gKDIgKiBzZS5maXQpLAogICAgICAgICAgICAgICAgIHVwclMgPSBmaXQgKyAoY3JpdCAqIHNlLmZpdCksCiAgICAgICAgICAgICAgICAgbHdyUyA9IGZpdCAtIChjcml0ICogc2UuZml0KQogICkKICByZXR1cm4ocHJlZCkKfQoKYGBgCgojIyBTZWxlY3Qgc21vb3RoaW5nIHBhcmFtZXRlcnMgd2l0aCBDVgpgYGB7ciBzbW9vdGhpbmcgcGFyYW1ldGVyIHNlbGVjdGlvbiBieSBDVn0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBwcmVwIGRhdGEgZm9yIHNwbGluZSBmaXRzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRsIDwtIGRsICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGNvbW11bml0eT1mYWN0b3IoY29tbXVuaXR5KSwKICAgICAgICAgZHVtbXk9MSkKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBjaG9vc2UgdGhlIHNtb290aGluZyBwYXJhbWV0ZXIKIyBmb3IgdGhlIHNwbGluZXMsIGssIAojIGJhc2VkIG9uIGNyb3NzLXZhbGlkYXRlZCBNU0UKIyBwaWNrIHNtYWxsZXN0IGsgd2hlcmUgQ1YgTVNFIGlzIAojIGNsb3NlIHRvIGl0cyBtaW5pbXVtCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgbGlicmFyeShTdXBlckxlYXJuZXIpCiMgbGlicmFyeShtZ2N2KQojIHNvdXJjZShoZXJlKCJSIiwiU0wubWdjdi5SIikpICMgbWdjdiB3cmFwcGVyIGZvciBTdXBlckxlYXJuZXIKIyAjIGs9NAojIHNldC5zZWVkKDEyMykKIyBzbGQgPC0gZmlsdGVyKGRsLGFudGlnZW49PSJtc3AxIikKIyBjdl9tc3AxIDwtIFN1cGVyTGVhcm5lcihZPXNsZCRsb2dtZmksWD1zZWxlY3Qoc2xkLGFnZSksIFNMLmxpYnJhcnkgPSBwYXN0ZSgiU0wubWdjdi5rIiw0OjEwLHNlcD0iIikpCiMgY3ZfbXNwMQojICMgaz00CiMgc2V0LnNlZWQoMTIzKQojIHNsZCA8LSBmaWx0ZXIoZGwsYW50aWdlbj09InZzcDUiKQojIGN2X3ZzcDUgPC0gU3VwZXJMZWFybmVyKFk9c2xkJGxvZ21maSxYPXNlbGVjdChzbGQsYWdlKSwgU0wubGlicmFyeSA9IHBhc3RlKCJTTC5tZ2N2LmsiLDQ6MTAsc2VwPSIiKSkKIyBjdl92c3A1CiMgIyBrPTQKIyBzZXQuc2VlZCgxMjMpCiMgc2xkIDwtIGZpbHRlcihkbCxhbnRpZ2VuPT0iYXNoYiIpCiMgY3ZfYXNoYiA8LSBTdXBlckxlYXJuZXIoWT1zbGQkbG9nbWZpLFg9c2VsZWN0KHNsZCxhZ2UpLCBTTC5saWJyYXJ5ID0gcGFzdGUoIlNMLm1nY3YuayIsNDoxMCxzZXA9IiIpKQojIGN2X2FzaGIKIyAjIGs9NAojIHNldC5zZWVkKDEyMykKIyBzbGQgPC0gZmlsdGVyKGRsLGFudGlnZW49PSJzZWEiKQojIGN2X3NlYSA8LSBTdXBlckxlYXJuZXIoWT1zbGQkbG9nbWZpLFg9c2VsZWN0KHNsZCxhZ2UpLCBTTC5saWJyYXJ5ID0gcGFzdGUoIlNMLm1nY3YuayIsNDoxMCxzZXA9IiIpKQojIGN2X3NlYQpgYGAKCiMjIEFnZSBkZXBlbmRlbnQgbWVhbnMKYGBge3IgYWdlY3VydmVzIG1maX0KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgZml0IEdBTSB3aXRoIGEgc3BsaW5lIGZvciBhZ2UKIyBpbmNsdWRlIGEgcmFuZG9tIGVmZmVjdCBmb3IgY2x1c3RlcgojIGVzdGltYXRlIHNpbXVsdGFuZW91cyBDSXMgYXJvdW5kIHRoZSBjdXJ2ZQojIGZvciB0aGUgcHJlZGljdGlvbiBkYXRhLCBzZXQgdGhlIGR1bW15IHRvIDAgdG8gCiMgemVybyBvdXQgYWxsIG9mIHRoZSByYW5kb20gZWZmZWN0cwojIHNlZSBwb3N0cyBvbiBTdGFjayBFeGNoYW5nZSBmb3IgZXhwbGFuYXRpb246CiMgaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMTMxMTA2L3ByZWRpY3Rpbmctd2l0aC1yYW5kb20tZWZmZWN0cy1pbi1tZ2N2LWdhbS8xMzExMTYjMTMxMTE2CiMgaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMTg5Mzg0L3ByZWRpY3RpbmctbWVhbi1zbW9vdGgtaW4tZ2FtLXdpdGgtc21vb3RoLWJ5LXJhbmRvbS1mYWN0b3ItaW50ZXJhY3Rpb24KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFBmIE1hbGFyaWEgTVNQMSAKZml0X21zcDEgPC0gbWdjdjo6Z2FtKGxvZ21maX5zKGFnZSxicz0iY3IiLGs9NCkgKyBzKGNvbW11bml0eSxicz0icmUiLGJ5PWR1bW15KSwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhPWZpbHRlcihkbCxhbnRpZ2VuPT0ibXNwMSIpKQpuZXdkIDwtIGRsICU+JSBmaWx0ZXIoYW50aWdlbj09Im1zcDEiKSAlPiUgbXV0YXRlKGR1bW15PTApCmZpdF9tc3AxY2kgPC0gZ2FtQ0kobT1maXRfbXNwMSxuZXdkYXRhPW5ld2QsbnJlcHM9MTAwMDApCgojIEdpYXJkaWEgVlNQNQpmaXRfdnNwNSA8LSBtZ2N2OjpnYW0obG9nbWZpfnMoYWdlLGJzPSJjciIsaz00KSArIHMoY29tbXVuaXR5LGJzPSJyZSIsYnk9ZHVtbXkpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZmlsdGVyKGRsLGFudGlnZW49PSJ2c3A1IikpCm5ld2QgPC0gZGwgJT4lIGZpbHRlcihhbnRpZ2VuPT0idnNwNSIpICU+JSBtdXRhdGUoZHVtbXk9MCkKZml0X3ZzcDVjaSA8LSBnYW1DSShtPWZpdF92c3A1LG5ld2RhdGE9bmV3ZCxucmVwcz0xMDAwMCkKCiMgQXNjYXJpcyBBc0hiCmZpdF9hc2hiIDwtIG1nY3Y6OmdhbShsb2dtZml+cyhhZ2UsYnM9ImNyIixrPTQpICsgcyhjb21tdW5pdHksYnM9InJlIixieT1kdW1teSksIAogICAgICAgICAgICAgICAgICAgICAgZGF0YT1maWx0ZXIoZGwsYW50aWdlbj09ImFzaGIiKSkKbmV3ZCA8LSBkbCAlPiUgZmlsdGVyKGFudGlnZW49PSJhc2hiIikgJT4lIG11dGF0ZShkdW1teT0wKQpmaXRfYXNoYmNpIDwtIGdhbUNJKG09Zml0X2FzaGIsbmV3ZGF0YT1uZXdkLG5yZXBzPTEwMDAwKQoKIyBTY2hpc3RvIFNFQQpmaXRfc2VhIDwtIG1nY3Y6OmdhbShsb2dtZml+cyhhZ2UsYnM9ImNyIixrPTQpICsgcyhjb21tdW5pdHksYnM9InJlIixieT1kdW1teSksIAogICAgICAgICAgICAgICAgICAgICAgZGF0YT1maWx0ZXIoZGwsYW50aWdlbj09InNlYSIpKQpuZXdkIDwtIGRsICU+JSBmaWx0ZXIoYW50aWdlbj09InNlYSIpICU+JSBtdXRhdGUoZHVtbXk9MCkKZml0X3NlYWNpIDwtIGdhbUNJKG09Zml0X3NlYSxuZXdkYXRhPW5ld2QsbnJlcHM9MTAwMDApCgpmaXRfbWZpIDwtIGJpbmRfcm93cyhmaXRfbXNwMWNpLGZpdF92c3A1Y2ksZml0X2FzaGJjaSxmaXRfc2VhY2kpCgpgYGAKCmBgYHtyIG1lYW4gbWZpIGJ5IGFnZSBwbG90fQoKcGFnZW1maSA8LSBnZ3Bsb3QoZGF0YT1maXRfbWZpLGFlcyh4PWFnZSx5PWZpdCxjb2xvcj1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKH5hbnRpZ2VuZikgKwogIGdlb21fcG9pbnQoYWVzKHg9YWdlLHk9bG9nbWZpKSxhbHBoYT0wLjEsc2l6ZT0wLjIpKwogIGdlb21fcmliYm9uKGFlcyh5bWluPWx3clMseW1heD11cHJTKSxhbHBoYT0wLjIsY29sb3I9TkEsZmlsbD0iYmxhY2siKSArCiAgZ2VvbV9saW5lKGx3ZD0wLjUsYWxwaGE9MC41LGNvbG9yPSJibGFjayIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIixzZT1GQUxTRSxsd2Q9MC41LGNvbG9yPSJibGFjayIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBjb2xzKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQsYnk9MSkpKwogIGxhYnMoeD0iYWdlLCB5ZWFycyIseT0ibG9nMTAgTHVtaW5leCBSZXNwb25zZSAoTUZJLWJnKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpwYWdlbWZpCgoKYGBgCgojIyBBZ2UgZGVwZW5kZW50IHNlcm9wcmV2YWxlbmNlCmBgYHtyIGFnZWN1cnZlcyBzZXJvcHJldn0KCgoKIyBBc2NhcmlzIEFzSGIKZml0cF9hc2hiIDwtIG1nY3Y6OmdhbShzZXJvcG9zfnMoYWdlLGJzPSJjciIsaz0xMSkgKyBzKGNvbW11bml0eSxicz0icmUiLGJ5PWR1bW15KSwgZmFtaWx5PSJiaW5vbWlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBkYXRhPWZpbHRlcihkbCxhbnRpZ2VuPT0iYXNoYiIpKQpuZXdkIDwtIGRsICU+JSBmaWx0ZXIoYW50aWdlbj09ImFzaGIiKSAlPiUgbXV0YXRlKGR1bW15PTApCmZpdHBfYXNoYmNpIDwtIGdhbUNJKG09Zml0cF9hc2hiLG5ld2RhdGE9bmV3ZCxucmVwcz0xMDAwMCkKCiMgU2NoaXN0byBTRUEKZml0cF9zZWEgPC0gbWdjdjo6Z2FtKHNlcm9wb3N+cyhhZ2UsYnM9ImNyIixrPTkpICsgcyhjb21tdW5pdHksYnM9InJlIixieT1kdW1teSksIGZhbWlseT0iYmlub21pYWwiLCAKICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZmlsdGVyKGRsLGFudGlnZW49PSJzZWEiKSkKbmV3ZCA8LSBkbCAlPiUgZmlsdGVyKGFudGlnZW49PSJzZWEiKSAlPiUgbXV0YXRlKGR1bW15PTApCmZpdHBfc2VhY2kgPC0gZ2FtQ0kobT1maXRwX3NlYSxuZXdkYXRhPW5ld2QsbnJlcHM9MTAwMDApCgojIFBmIE1hbGFyaWEgTVNQMSAKZml0cF9tc3AxIDwtIG1nY3Y6OmdhbShzZXJvcG9zfnMoYWdlLGJzPSJjciIsaz00KSArIHMoY29tbXVuaXR5LGJzPSJyZSIsYnk9ZHVtbXkpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZmlsdGVyKGRsLGFudGlnZW49PSJtc3AxIikpCm5ld2QgPC0gZGwgJT4lIGZpbHRlcihhbnRpZ2VuPT0ibXNwMSIpICU+JSBtdXRhdGUoZHVtbXk9MCkKZml0cF9tc3AxY2kgPC0gZ2FtQ0kobT1maXRwX21zcDEsbmV3ZGF0YT1uZXdkLG5yZXBzPTEwMDAwKQoKIyBHaWFyZGlhIFZTUDUKZml0cF92c3A1IDwtIG1nY3Y6OmdhbShzZXJvcG9zfnMoYWdlLGJzPSJjciIsaz00KSArIHMoY29tbXVuaXR5LGJzPSJyZSIsYnk9ZHVtbXkpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZmlsdGVyKGRsLGFudGlnZW49PSJ2c3A1IikpCm5ld2QgPC0gZGwgJT4lIGZpbHRlcihhbnRpZ2VuPT0idnNwNSIpICU+JSBtdXRhdGUoZHVtbXk9MCkKZml0cF92c3A1Y2kgPC0gZ2FtQ0kobT1maXRwX3ZzcDUsbmV3ZGF0YT1uZXdkLG5yZXBzPTEwMDAwKQoKCmZpdF9zZXJvcHJldiA8LSBiaW5kX3Jvd3MoZml0cF9hc2hiY2ksZml0cF9zZWFjaSxmaXRwX21zcDFjaSxmaXRwX3ZzcDVjaSkKCiMgY29udmVydCBsaW5lYXIgcHJlZGljdG9yIHRvIHByZXZhbGFuY2UKZXhwaXRmbiA8LSBmdW5jdGlvbih4KSB7CiAgZXhwKHgpLygxK2V4cCh4KSkKfQpmaXRfc2Vyb3ByZXYgPC0gZml0X3Nlcm9wcmV2ICU+JQogIG11dGF0ZShmaXQgPSBleHBpdGZuKGZpdCksCiAgICAgICAgIHVwclAgPSBleHBpdGZuKHVwclApLAogICAgICAgICBsd3JQID0gZXhwaXRmbihsd3JQKSwKICAgICAgICAgdXByUyA9IGV4cGl0Zm4odXByUyksCiAgICAgICAgIGx3clMgPSBleHBpdGZuKGx3clMpLAogICAgICAgICApCgpgYGAKCmBgYHtyIHNlcm9wcmV2IGJ5IGFnZSBwbG90fQoKcGFnZXByZXYgPC0gZ2dwbG90KGRhdGE9Zml0X3Nlcm9wcmV2LGFlcyh4PWFnZSx5PWZpdCxjb2xvcj1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKH5hbnRpZ2VuZikgKwogIGdlb21fcmliYm9uKGFlcyh5bWluPWx3clMseW1heD11cHJTKSxhbHBoYT0wLjIsY29sb3I9TkEsZmlsbD0iZ3JleTUwIikgKwogIGdlb21fbGluZShsd2Q9MC41LGFscGhhPTAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLHNlPUZBTFNFLGx3ZD0wLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBjb2xzKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDAuOCxieT0wLjEpLGxhYmVscz1zZXEoMCw4MCxieT0xMCkpKwogIGxhYnMoeD0iYWdlLCB5ZWFycyIseT0iU2Vyb3ByZXZhbGVuY2UgKCUpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnBhZ2VwcmV2CgoKYGBgCiMgQ2x1c3RlciBsZXZlbCBzZXJvcHJldmFsZW5jZSB2cy4gbWVhbnMKCmBgYHtyIGNsdXN0ZXIgbWVhbnN9CgpkbGMgPC0gZGwgJT4lCiAgZ3JvdXBfYnkoY29tbXVuaXR5LGFudGlnZW5mKSAlPiUKICBtdXRhdGUocG9zcm9jPWlmZWxzZShsb2dtZmk+cm9jY3V0LDEsMCkpICU+JQogIHN1bW1hcml6ZShuPW4oKSwKICAgICAgICAgICAgbWVhbm1maT1tZWFuKGxvZ21maSksCiAgICAgICAgICAgIHByZXZyb2M9bWVhbihwb3Nyb2MpKQoKIyBlc3RpbWF0ZSBwZWFyc29uIGNvcnJlbGF0aW9uCmRjb3JyIDwtIGRsYyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoYW50aWdlbmYpICU+JQogIG11dGF0ZShjb3Jyb2M9Y29yKG1lYW5tZmkscHJldnJvYyxtZXRob2Q9InBlYXJzb24iKSkgJT4lCiAgc2xpY2UoMSkKCiMgZXN0aW1hdGUgcmVncmVzc2lvbiBzbG9wZQpmb3JlYWNoKGFpPWxldmVscyhkbGMkYW50aWdlbmYpKSAlZG8lIHsKICBkaSA8LSBkbGMgJT4lIGZpbHRlcihhbnRpZ2VuZj09YWkpCiAgZml0IDwtIGxtKHByZXZyb2N+bWVhbm1maSxkYXRhPWRpKQogIHN1bW1hcnkoZml0KQp9CmBgYAoKCmBgYHtyIHByZXYgdnMgbWZpIGZpZ3VyZX0KcG1maXZwcmV2IDwtIGdncGxvdChkYXRhPWRsYyxhZXMoeD1tZWFubWZpLGNvbG9yPWFudGlnZW5mKSkgKwogIGZhY2V0X2dyaWQofmFudGlnZW5mKSArCiAgZ2VvbV9wb2ludChhZXMoeT1wcmV2cm9jKSxhbHBoYT0wLjcpICsKICBnZW9tX3Ntb290aChhZXMoeT1wcmV2cm9jKSxtZXRob2Q9ImdsbSIsY29sb3I9ImdyYXk0MCIsbHdkPTAuNSxzZT1GQUxTRSkgKwogIGdlb21fdGV4dChkYXRhPWRjb3JyLAogICAgICAgICAgICBhZXMoeD0yLjUseT0wLjk1LGxhYmVsPXBhc3RlKCJyID09IixzcHJpbnRmKCIlMS4yZiIsY29ycm9jKSkgKSwKICAgICAgICAgICAgcGFyc2U9VFJVRSxjb2w9ImdyZXkzMCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDEsYnk9MC4yKSxsYWJlbHM9c2VxKDAsMTAwLGJ5PTIwKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1wY29scykrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEpLHhsaW09YygxLDQuNSkpICsKICBsYWJzKHg9ImNvbW11bml0eSBtZWFuIGxvZzEwIEx1bWluZXggcmVzcG9uc2UgKE1GSS1iZykiLHk9ImNvbW11bml0eSBzZXJvcHJldmFsZW5jZSAoJSkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwbWZpdnByZXYKCiMgc2F2ZSBwbmcgZmlsZSBmb3IgcHJlc2VudGF0aW9uCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCJtYml0YS1tZmktdi1zZXJvcHJldi5wbmciKSxwbG90PXBtZml2cHJldixkZXZpY2U9InBuZyIsd2lkdGg9OCxoZWlnaHQ9Mi41KQoKCmBgYAoKIyBDb21wYXJpc29uIHdpdGggRk9JCgojIyBDb25zdGFudCByYXRlIG1vZGVsCkVzdGltYXRlIGNvbW11bml0eS1sZXZlbCBGT0kgZm9yIHBhdGhvZ2VucyBiYXNlZCBvbiB0aGUgc2Vyb2NvbnZlcnNpb24gcmF0ZSAoaW5jaWRlbmNlIGFtb25nIHN1c2NlcHRpYmxlcykuIEFzc3VtZSBhIGNvbnN0YW50IHJhdGUgbW9kZWwgKGkuZS4sIGF2ZXJhZ2Ugb3ZlciBhbGwgYWdlcykuICBJZiB3ZSBhc3N1bWUgYSBjb25zdGFudCBmb3JjZSBvZiBpbmZlY3Rpb24gKCRcbGFtYmRhJCksIGVxdWl2YWxlbnQgdG8gYXNzdW1pbmcgYW4gZXhwb25lbnRpYWwgbW9kZWwsIHRoZW4gaXQgY2FuIGJlIHNob3duIHRoYXQgYSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgd2l0aCBhIGNvbXBsZW1lbnRhcnkgbG9nLWxvZyBsaW5rIGZpdCB3aXRoIGN1cnJlbnQgc3RhdHVzLCBhZ2UtcHJldmFsZW5jZSBkYXRhIGlzIGVxdWl2YWxlbnQgdG8gYW4gZXhwb25lbnRpYWwgcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWwgKCpKZXdlbGwgYW5kIHZhbiBkZXIgTGFhbiAxOTk1KikuIAoKJFxsb2cgLSBcbG9nKDEtUChZfEEsVykpID0gXGxvZyBcbGFtYmRhICsgXGxvZyBBICsgXGJldGEgVyQKCk1vcm9ldmVyLCB0aGlzIG1vZGVsIGlzIGFsc28gZXF1aXZhbGVudCB0byBhIGNhdGFseXRpYywgU0lSIG1vZGVsIHdpdGggYSBzaW5nbGUsIGNvbnN0YW50IHJhdGUgcGFyYW1ldGVyICgqSGVucyBldCBhbC4gMjAxMCo7ICpIZW5zIGV0IGFsLiAyMDEyKikuCgpgYGB7ciAgZm9pIGVzdGltYXRpb24gZm9yIG1zcCBhbmQgc2VhfQoKIyBmb2lfZXN0cyA8LSBmb3JlYWNoKGFpPWMoIlMuIG1hbnNvbmkgU0VBIiwiUC4gZmFsY2lwYXJ1bSBNU1AtMSIpLC5jb21iaW5lPXJiaW5kKSAlOiUKZm9pX2VzdHMgPC0gZm9yZWFjaChhaT1sZXZlbHMoZGwkYW50aWdlbmYpLC5jb21iaW5lPXJiaW5kKSAlOiUKICBmb3JlYWNoKGNvbWk9bGV2ZWxzKGRsJGNvbW11bml0eSksLmNvbWJpbmU9cmJpbmQpICVkbyUgewogIAogIGRpIDwtIGRsICU+JSBmaWx0ZXIoYW50aWdlbmY9PWFpICYgY29tbXVuaXR5PT1jb21pKQogIGdmaXQgPC0gZ2xtKHNlcm9wb3N+MSxvZmZzZXQ9bG9nKGFnZSksZGF0YT1kaSxmYW1pbHk9Ymlub21pYWwobGluaz0iY2xvZ2xvZyIpKQogIGdzdW0gPC0gc3VtbWFyeShnZml0KQogIGxhbWJkYSA8LSBhcy5udW1lcmljKGV4cChnZml0JGNvZWZmaWNpZW50cykpCiAgbG9nX2xhbWJkYV9zZSAgPC0gc3FydChnc3VtJGNvdi51bnNjYWxlZCkKICBsYW1iZGFfbGIgPC0gYXMubnVtZXJpYyhleHAoZ2ZpdCRjb2VmZmljaWVudHMgLSAxLjk2KmxvZ19sYW1iZGFfc2UpKQogIGxhbWJkYV91YiA8LSBhcy5udW1lcmljKGV4cChnZml0JGNvZWZmaWNpZW50cyArIDEuOTYqbG9nX2xhbWJkYV9zZSkpCiAgcmVzIDwtIGRhdGEuZnJhbWUoY29tbXVuaXR5PWNvbWksYW50aWdlbmY9YWksbGFtYmRhLGxhbWJkYV9sYixsYW1iZGFfdWIpCiAgcmV0dXJuKHJlcykKfQoKZm9pX2VzdHMKCmBgYAoKCgpgYGB7ciBtZXJnZSBmb2kgZXN0aW1hdGVzfQojIG1lcmdlIEZPSSBlc3RpbWF0ZXMgdG8gdGhlIGNsdXN0ZXItbGV2ZWwgbWVhbnMKZF9mb2kgPC0gZGxjICU+JQogICMgZmlsdGVyKGFudGlnZW5mPT0iUy4gbWFuc29uaSBTRUEifGFudGlnZW5mPT0iUC4gZmFsY2lwYXJ1bSBNU1AtMSIpICU+JQogIGxlZnRfam9pbihmb2lfZXN0cyxieT1jKCJjb21tdW5pdHkiLCJhbnRpZ2VuZiIpKSMgJT4lCiAgIyBtdXRhdGUoYW50aWdlbmY9ZmFjdG9yKGFudGlnZW5mLGxldmVscz1jKCJTLiBtYW5zb25pIFNFQSIsIlAuIGZhbGNpcGFydW0gTVNQLTEiKSkpCgoKIyBlc3RpbWF0ZSBzcGVhcm1hbiBjb3JyZWxhdGlvbgpkZm9pY29yciA8LSBkX2ZvaSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoYW50aWdlbmYpICU+JQogIG11dGF0ZShjb3JtZmk9Y29yKG1lYW5tZmksbGFtYmRhLG1ldGhvZD0ic3BlYXJtYW4iKSwKICAgICAgICAgY29ycHJldj1jb3IocHJldnJvYyxsYW1iZGEsbWV0aG9kPSJzcGVhcm1hbiIpKSAlPiUKICBzbGljZSgxKQoKYGBgCgpGaWd1cmUgb2YgcmVzdWx0cwpgYGB7ciBmb2kgdnMgbWZpIGZpZ3VyZX0KcGZvaXZtZmkgPC0gZ2dwbG90KGRhdGE9ZF9mb2ksYWVzKHg9bWVhbm1maSx5PWxhbWJkYSxjb2xvcj1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKH5hbnRpZ2VuZikrCiAgZ2VvbV9wb2ludCgsYWxwaGE9MC43KSArCiAgIyBnZW9tX3Ntb290aChtZXRob2Q9ImdsbSIsY29sb3I9ImdyYXk0MCIsbHdkPTAuNSxzZT1GQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLGNvbG9yPSJncmF5NDAiLGx3ZD0wLjUsc2U9RkFMU0UpICsKICBnZW9tX3RleHQoZGF0YT1kZm9pY29yciwKICAgICAgICAgICAgYWVzKHg9Mi41LHk9MC42LGxhYmVsPXBhc3RlKCJyaG8gPT0iLHNwcmludGYoIiUxLjJmIixjb3JtZmkpKSApLAogICAgICAgICAgICBwYXJzZT1UUlVFLGNvbD0iZ3JleTMwIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9Mjo0KSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBjb2xzKSsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDEuNSw0KSx5bGltPWMoMCwwLjcpKSArCiAgbGFicyh4PSJjb21tdW5pdHkgbWVhbiBsb2cxMCBMdW1pbmV4IHJlc3BvbnNlIChNRkktYmcpIix5PWV4cHJlc3Npb24ocGFzdGUoImNvbW11bml0eSBzZXJvY29udmVyc2lvbiByYXRlICgiLGxhbWJkYSwiKSIpKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcGZvaXZtZmkKCiMgc2F2ZSBwbmcgZmlsZSBmb3IgcHJlc2VudGF0aW9uCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCJtYml0YS1tZmktdi1mb2kucG5nIikscGxvdD1wZm9pdm1maSxkZXZpY2U9InBuZyIsd2lkdGg9OCxoZWlnaHQ9Mi41KQoKYGBgCmBgYHtyIGZvaSB2cyBwcmV2IGZpZ3VyZX0KcGZvaXZwcmV2IDwtIGdncGxvdChkYXRhPWRfZm9pLGFlcyh4PXByZXZyb2MseT1sYW1iZGEsY29sb3I9YW50aWdlbmYpKSArCiAgZmFjZXRfZ3JpZCh+YW50aWdlbmYpKwogIGdlb21fcG9pbnQoYWxwaGE9MC43KSArCiAgIyBnZW9tX3Ntb290aChtZXRob2Q9ImdsbSIsY29sb3I9ImdyYXk0MCIsbHdkPTAuNSxzZT1GQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLGNvbG9yPSJncmF5NDAiLGx3ZD0wLjUsc2U9RkFMU0UpICsKICBnZW9tX3RleHQoZGF0YT1kZm9pY29yciwKICAgICAgICAgICAgYWVzKHg9MC4yLHk9MC42LGxhYmVsPXBhc3RlKCJyaG8gPT0iLHNwcmludGYoIiUxLjJmIixjb3JwcmV2KSkgKSwKICAgICAgICAgICAgcGFyc2U9VFJVRSxjb2w9ImdyZXkzMCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDEsYnk9MC4yKSxsYWJlbHM9c2VxKDAsMTAwLGJ5PTIwKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1wY29scykrCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDEpLHlsaW09YygwLDAuNykpICsKICBsYWJzKHg9ImNvbW11bml0eSBzZXJvcHJldmFsZW5jZSAoJSkiLHk9ZXhwcmVzc2lvbihwYXN0ZSgiY29tbXVuaXR5IHNlcm9jb252ZXJzaW9uIHJhdGUgKCIsbGFtYmRhLCIpIikpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwZm9pdnByZXYKCiMgc2F2ZSBwbmcgZmlsZSBmb3IgcHJlc2VudGF0aW9uCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCJtYml0YS1wcmV2LXYtZm9pLnBuZyIpLHBsb3Q9cGZvaXZwcmV2LGRldmljZT0icG5nIix3aWR0aD04LGhlaWdodD0zKQpgYGAKCiMgQ29tcGFyaXNvbiB3aXRoIEthdG8tS2F0egoKYGBge3IgY2x1c3RlciBtZWFucyB2cyBra30KCmRsY2trIDwtIGRsICU+JQogIGdyb3VwX2J5KGNvbW11bml0eSxhbnRpZ2VuZikgJT4lCiAgZmlsdGVyKGFudGlnZW4gJWluJSBjKCJhc2hiIiwic2VhIikpICU+JQogIG11dGF0ZShwb3Nyb2M9aWZlbHNlKGxvZ21maT5yb2NjdXQsMSwwKSwKICAgICAgICAgbG9na2s9aWZlbHNlKGVwZz4wLGxvZzEwKGVwZyksbG9nMTAoMSkpLAogICAgICAgICBwb3Nraz1pZmVsc2UoZXBnPjAsMSwwKSkgJT4lCiAgc3VtbWFyaXplKG49bigpLAogICAgICAgICAgICBtZWFubWZpPW1lYW4obG9nbWZpKSwKICAgICAgICAgICAgcHJldnJvYz1tZWFuKHBvc3JvYyksCiAgICAgICAgICAgIG1lYW5raz1tZWFuKGxvZ2trLG5hLnJtPVQpLAogICAgICAgICAgICBwcmV2a2s9bWVhbihwb3NrayxuYS5ybT1UKSkKCgojIGVzdGltYXRlIHBlYXJzb24gY29ycmVsYXRpb24KZGNvcnJrayA8LSBkbGNrayAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoYW50aWdlbmYpICU+JQogIG11dGF0ZShjb3JzcGtrPWNvcihwcmV2cm9jLHByZXZrayxtZXRob2Q9InNwZWFybWFuIiksCiAgICAgICAgIGNvcm11a2s9Y29yKG1lYW5tZmkscHJldmtrLG1ldGhvZD0ic3BlYXJtYW4iKSwKICAgICAgICAgY29ybXVra211PWNvcihtZWFubWZpLG1lYW5rayxtZXRob2Q9InNwZWFybWFuIikpICU+JQogIHNsaWNlKDEpCgpgYGAKCkZpZ3VyZSBvZiByZXN1bHRzCgpgYGB7ciBtZmkgYW5kIHByZXYgdnMga2t9CiMgS0sgcHJldmFsZW5jZSB2ZXJzdXMgTUZJCnBtZml2a2sgPC0gZ2dwbG90KGRhdGE9ZGxja2ssYWVzKHg9bWVhbm1maSxjb2xvcj1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKH5hbnRpZ2VuZikgKwogIGdlb21fcG9pbnQoYWVzKHk9cHJldmtrKSxhbHBoYT0wLjcpICsKICBnZW9tX3Ntb290aChhZXMoeT1wcmV2a2spLG1ldGhvZD0ibG9lc3MiLGNvbG9yPSJncmF5NDAiLGx3ZD0wLjUsc2U9RkFMU0UpICsKICBnZW9tX3RleHQoZGF0YT1kY29ycmtrLAogICAgICAgICAgICBhZXMoeD0xLHk9MC41NSxsYWJlbD1wYXN0ZSgicmhvID09IixzcHJpbnRmKCIlMS4yZiIsY29ybXVraykpICksCiAgICAgICAgICAgIHBhcnNlPVRSVUUsY29sPSJncmV5MzAiLGhqdXN0PTApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDAuNixieT0wLjIpLGxhYmVscz1zZXEoMCw2MCxieT0yMCkpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9cGNvbHMpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwwLjYpLHhsaW09YygxLDQuNSkpICsKICBsYWJzKHg9ImNvbW11bml0eSBtZWFuIGxvZzEwIEx1bWluZXggcmVzcG9uc2UiLHk9ImNvbW11bml0eSBwcmV2YWxlbmNlIGJ5IEthdG8tS2F0eiAoJSkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwbWZpdmtrCgojIHNhdmUgcG5nIGZpbGUgZm9yIHByZXNlbnRhdGlvbgpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwibWJpdGEtbWZpLXYta2twcmV2LnBuZyIpLHBsb3Q9cG1maXZrayxkZXZpY2U9InBuZyIsd2lkdGg9NSxoZWlnaHQ9MykKCgojIEtLIG1lYW4gdmVyc3VzIE1GSQpwbWZpdmtrbWVhbiA8LSBnZ3Bsb3QoZGF0YT1kbGNrayxhZXMoeD1tZWFubWZpLGNvbG9yPWFudGlnZW5mKSkgKwogIGZhY2V0X2dyaWQofmFudGlnZW5mKSArCiAgZ2VvbV9wb2ludChhZXMoeT1tZWFua2spLGFscGhhPTAuNykgKwogIGdlb21fc21vb3RoKGFlcyh5PW1lYW5rayksbWV0aG9kPSJsb2VzcyIsY29sb3I9ImdyYXk0MCIsbHdkPTAuNSxzZT1GQUxTRSkgKwogIGdlb21fdGV4dChkYXRhPWRjb3Jya2ssCiAgICAgICAgICAgIGFlcyh4PTEseT0xLGxhYmVsPXBhc3RlKCJyaG8gPT0iLHNwcmludGYoIiUxLjJmIixjb3JtdWtrbXUpKSApLAogICAgICAgICAgICBwYXJzZT1UUlVFLGNvbD0iZ3JleTMwIixoanVzdD0wKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxLGJ5PTAuMikpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9cGNvbHMpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwxLjEpLHhsaW09YygxLDQuNSkpICsKICBsYWJzKHg9ImNvbW11bml0eSBtZWFuIGxvZzEwIEx1bWluZXggcmVzcG9uc2UiLHk9ImNvbW11bml0eSBtZWFuIGxvZzEwIGVnZ3MgcGVyIGdyYW0iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwbWZpdmtrbWVhbgoKIyBzYXZlIHBuZyBmaWxlIGZvciBwcmVzZW50YXRpb24KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsIm1iaXRhLW1maS12LWtrbWVhbi5wbmciKSxwbG90PXBtZml2a2ttZWFuLGRldmljZT0icG5nIix3aWR0aD01LGhlaWdodD0zKQoKIyBLSyBwcmV2YWxlbmNlIHZlcnN1cyBzZXJvcHJldmFsZW5jZQpwc3B2a2sgPC0gZ2dwbG90KGRhdGE9ZGxja2ssYWVzKHg9cHJldnJvYyxjb2xvcj1hbnRpZ2VuZikpICsKICBmYWNldF9ncmlkKH5hbnRpZ2VuZikgKwogIGdlb21fcG9pbnQoYWVzKHk9cHJldmtrKSxhbHBoYT0wLjcpICsKICBnZW9tX3Ntb290aChhZXMoeT1wcmV2a2spLG1ldGhvZD0ibG9lc3MiLGNvbG9yPSJncmF5NDAiLGx3ZD0wLjUsc2U9RkFMU0UpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQ9MCxzbG9wZT0xLGx0eT0iZGFzaGVkIixjb2xvcj0iZ3JheTQwIixsd2Q9MC4yNSkrCiAgZ2VvbV90ZXh0KGRhdGE9ZGNvcnJraywKICAgICAgICAgICAgYWVzKHg9MC4xLHk9MC44LGxhYmVsPXBhc3RlKCJyaG8gPT0iLHNwcmludGYoIiUxLjJmIixjb3JzcGtrKSkgKSwKICAgICAgICAgICAgcGFyc2U9VFJVRSxjb2w9ImdyZXkzMCIsaGp1c3Q9MCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMC44LGJ5PTAuMiksbGFiZWxzPXNlcSgwLDgwLGJ5PTIwKSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwwLjgsYnk9MC4yKSxsYWJlbHM9c2VxKDAsODAsYnk9MjApKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBjb2xzKSsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMC45KSx4bGltPWMoMCwwLjkpKSArCiAgbGFicyh4PSJjb21tdW5pdHkgc2Vyb3ByZXZhbGVuY2UgKCUpIix5PSJjb21tdW5pdHkgcHJldmFsZW5jZSBieSBLYXRvLUthdHogKCUpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcHNwdmtrCgojIHNhdmUgcG5nIGZpbGUgZm9yIHByZXNlbnRhdGlvbgpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwibWJpdGEtc2Vyb3Atdi1ra3ByZXYucG5nIikscGxvdD1wc3B2a2ssZGV2aWNlPSJwbmciLHdpZHRoPTUsaGVpZ2h0PTMpCgoKCgpgYGAKCgojIEVmZmVjdCBvZiBzbWFsbGVyIHNhbXBsZXMKCkRvd25zYW1wbGUgdGhlIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaCBjb21tdW5pdHkgdG8gc2VlIGhvdyB0aGF0IGluZmx1ZW5jZXMgdGhlIHJlbGF0aW9uc2hpcHMKCk5vdGU6IHRoaXMgYW5hbHlzaXMgcHJvYmFibHkgd29uJ3QgY29udHJpYnV0ZSB0byB0aGUgcHJlc2VudGF0aW9uIGJlY2F1c2Ugbm90IHN1ZmZpY2llbnQgdGltZSB0byB0aGluayBhYm91dCBpdCBhbmQgaW50ZXJwcmV0IGl0LiAKCmBgYHtyIGNvbW11bml0eSBkb3duc2FtcGxlfQoKIyBlc3RpbWF0ZSBtZWFucyB3aXRoIHNhbXBsZSBzaXplcyBvZiBiZXR3ZWVlbiAxMCBhbmQgNTAgb2JzZXJ2YXRpb25zCnNldC5zZWVkKDEyMykKc3Nlc3RzIDwtIGZvcmVhY2goc3NpPXNlcSgxMDAsMTAsYnk9LTEwKSwuY29tYmluZT1yYmluZCkgJTolIAogIGZvcmVhY2goaXRlcmk9MToxMDAwLC5jb21iaW5lPXJiaW5kKSAlZG9wYXIlIHsKICAgIGRpIDwtIGRsICU+JQogICAgZ3JvdXBfYnkoY29tbXVuaXR5LGFudGlnZW5mKSAlPiUKICAgIHNhbXBsZV9uKHNpemU9c3NpLHJlcGxhY2U9VFJVRSkgJT4lCiAgICBzdW1tYXJpemUobWVhbm1maT1tZWFuKGxvZ21maSksCiAgICAgICAgICAgICAgc2Vyb3ByZXY9bWVhbihzZXJvcG9zKSkgJT4lCiAgICBtdXRhdGUoc3M9c3NpLGl0ZXI9aXRlcmkpCn0KCiMgbWVyZ2UgaW4gdGhlIG1lYW5zIHdpdGggdGhlIGZ1bGwgc2FtcGxlCmRsYzIgPC0gZGxjICU+JQogIHNlbGVjdChjb21tdW5pdHksYW50aWdlbmYsbixtZWFuPW1lYW5tZmksc2Vyb3A9cHJldnJvYykgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkX2ZvaSxjb21tdW5pdHksYW50aWdlbmYsbGFtYmRhKSxieT1jKCJjb21tdW5pdHkiLCJhbnRpZ2VuZiIpKSAlPiUKICBtdXRhdGUoYW50aWdlbmY9ZmFjdG9yKGFudGlnZW5mLGxldmVscz1sZXZlbHMoZGwkYW50aWdlbmYpKSkKc3Nlc3RzMiA8LSBsZWZ0X2pvaW4oc3Nlc3RzLGRsYzIsYnk9YygiY29tbXVuaXR5IiwiYW50aWdlbmYiKSkKCgojIGF2ZXJhZ2Ugb3ZlciBib290c3RyYXAgZXN0aW1hdGVzCnNzbWVhbnMgPC0gc3Nlc3RzMiAlPiUKICBncm91cF9ieShjb21tdW5pdHksYW50aWdlbmYsc3MpICU+JQogIHN1bW1hcml6ZShtZWFuPW1lYW4obWVhbiksCiAgICAgICAgICAgIG11PW1lYW4obWVhbm1maSksCiAgICAgICAgICAgIG11X3NkPXNkKG1lYW5tZmkpLAogICAgICAgICAgICBtdV9sYj1xdWFudGlsZShtZWFubWZpLHByb2JzPWMoMC4wMjUpKSwKICAgICAgICAgICAgbXVfdWI9cXVhbnRpbGUobWVhbm1maSxwcm9icz1jKDAuOTc1KSksCiAgICAgICAgICAgIG11X21zZT1tZWFuKChtZWFubWZpLW1lYW4pXjIpLAogICAgICAgICAgICBtdV9iaWFzPW1lYW4obWVhbm1maS1tZWFuKSwKICAgICAgICAgICAgCiAgICAgICAgICAgIHNlcm9wPW1lYW4oc2Vyb3ApLAogICAgICAgICAgICBwcmV2PW1lYW4oc2Vyb3ByZXYpLAogICAgICAgICAgICBwcmV2X3NkPXNkKHNlcm9wcmV2KSwKICAgICAgICAgICAgcHJldl9sYj1xdWFudGlsZShzZXJvcHJldixwcm9icz1jKDAuMDI1KSksCiAgICAgICAgICAgIHByZXZfdWI9cXVhbnRpbGUoc2Vyb3ByZXYscHJvYnM9YygwLjk3NSkpLAogICAgICAgICAgICBwcmV2X21zZT1tZWFuKChzZXJvcHJldi1zZXJvcCleMiksCiAgICAgICAgICAgIHByZXZfYmlhcz1tZWFuKHNlcm9wcmV2LXNlcm9wKQogICAgICAgICAgICApCgojIGNvcnJlbGF0aW9uIGJldHdlbiBjb21tdW5pdHktbGV2ZWwgbWVhbnMgYW5kIHNlcm9wcmV2LCBvdmVyIGJvb3RzdHJhcCBlc3RpbWF0ZXMKc3Njb3JyIDwtIHNzZXN0czIgJT4lCiAgZ3JvdXBfYnkoYW50aWdlbmYsc3MsaXRlcikgJT4lCiAgc3VtbWFyaXplKGNvcl9tZWFuPWNvcihtZWFubWZpLHNlcm9wLG1ldGhvZD0icGVhcnNvbiIpLAogICAgICAgICAgICBjb3JfcHJldj1jb3Ioc2Vyb3ByZXYsc2Vyb3AsbWV0aG9kPSJwZWFyc29uIikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShhbnRpZ2VuZixzcykgJT4lCiAgc3VtbWFyaXplKGNvcl9tdT1tZWFuKGNvcl9tZWFuKSwKICAgICAgICAgICAgY29yX211X2xiPXF1YW50aWxlKGNvcl9tZWFuLHByb2JzPWMoMC4wMjUpKSwKICAgICAgICAgICAgY29yX211X3ViPXF1YW50aWxlKGNvcl9tZWFuLHByb2JzPWMoMC45NzUpKSwKICAgICAgICAgICAgY29yX3ByPW1lYW4oY29yX3ByZXYpLAogICAgICAgICAgICBjb3JfcHJfbGI9cXVhbnRpbGUoY29yX3ByZXYscHJvYnM9YygwLjAyNSkpLAogICAgICAgICAgICBjb3JfcHJfdWI9cXVhbnRpbGUoY29yX3ByZXYscHJvYnM9YygwLjk3NSkpLAogICAgICAgICAgICApCgojIGNvcnJlbGF0aW9uIGJldHdlbiBjb21tdW5pdHktbGV2ZWwgbWVhbnMgYW5kIHNlcm9wcmV2LCBvdmVyIGJvb3RzdHJhcCBlc3RpbWF0ZXMKc3Njb3JyX2ZvaSA8LSBzc2VzdHMyICU+JQogIGdyb3VwX2J5KGFudGlnZW5mLHNzLGl0ZXIpICU+JQogIGZpbHRlcighaXMubmEobGFtYmRhKSkgJT4lCiAgc3VtbWFyaXplKGNvcl9tZWFuPWNvcihtZWFubWZpLGxhbWJkYSxtZXRob2Q9InNwZWFybWFuIiksCiAgICAgICAgICAgIGNvcl9wcmV2PWNvcihzZXJvcHJldixsYW1iZGEsbWV0aG9kPSJzcGVhcm1hbiIpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoYW50aWdlbmYsc3MpICU+JQogIHN1bW1hcml6ZShjb3JfbXU9bWVhbihjb3JfbWVhbiksCiAgICAgICAgICAgIGNvcl9tdV9sYj1xdWFudGlsZShjb3JfbWVhbixwcm9icz1jKDAuMDI1KSksCiAgICAgICAgICAgIGNvcl9tdV91Yj1xdWFudGlsZShjb3JfbWVhbixwcm9icz1jKDAuOTc1KSksCiAgICAgICAgICAgIGNvcl9wcj1tZWFuKGNvcl9wcmV2KSwKICAgICAgICAgICAgY29yX3ByX2xiPXF1YW50aWxlKGNvcl9wcmV2LHByb2JzPWMoMC4wMjUpKSwKICAgICAgICAgICAgY29yX3ByX3ViPXF1YW50aWxlKGNvcl9wcmV2LHByb2JzPWMoMC45NzUpKSwKICAgICAgICAgICAgKQoKYGBgClBsb3QgYSBmaWd1cmUgb2YgUk1TRSBpbiBjb21tdW5pdHktbGV2ZWwgZXN0aW1hdGVzIGJ5IHNhbXBsZSBzaXplLgpgYGB7ciBkb3duc2FtcGxlIHJtc2UgZmlnc30Kc3NtdW1zZXAgPC0gZ2dwbG90KGRhdGE9c3NtZWFucyxhZXMoeD1zcyx5PXNxcnQobXVfbXNlKSxncm91cD1jb21tdW5pdHkpKSArCiAgZmFjZXRfZ3JpZCgufmFudGlnZW5mKSsKICBnZW9tX2xpbmUoYWxwaGE9MC4yKSArCiAgdGhlbWVfbWluaW1hbCgpCnNzbXVtc2VwCgpzc3ByZXZtc2VwIDwtIGdncGxvdChkYXRhPXNzbWVhbnMsYWVzKHg9c3MseT1zcXJ0KHByZXZfbXNlKSxncm91cD1jb21tdW5pdHkpKSArCiAgZmFjZXRfZ3JpZCgufmFudGlnZW5mKSsKICBnZW9tX2xpbmUoYWxwaGE9MC4yKSArCiAgdGhlbWVfbWluaW1hbCgpCnNzcHJldm1zZXAKCmBgYAoKUGxvdCBhIGZpZ3VyZSBvZiB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgc2Vyb3ByZXZhbGVuY2UgZXN0aW1hdGVkIGluIHRoZSBmdWxsIHNhbXBsZSB2ZXJzdXMgY2x1c3RlciBsZXZlbCBtZWFuIChibHVlKSBvciBjbHVzdGVyIGxldmVsIHNlcm9wcmV2YWxlbmNlIChvcmFuZ2UpIGVzdGltYXRlZCBhdCBlYWNoIHNtYWxsZXIgc2FtcGxlIHNpemUKCmBgYHtyIGRvd25zYW1wbGUgY29yciBmaWd1cmV9CnNzY29ycCA8LSBnZ3Bsb3QoZGF0YT1zc2NvcnIsYWVzKHg9c3MpKSArCiAgZmFjZXRfZ3JpZCgufmFudGlnZW5mKSsKICBnZW9tX3JpYmJvbihhZXMoeW1pbj1jb3JfbXVfbGIseW1heD1jb3JfbXVfdWIpLGNvbG9yPU5BLGZpbGw9Y2JsdWUsYWxwaGE9MC4yKSsKICBnZW9tX2xpbmUoYWVzKHk9Y29yX211KSxjb2xvcj1jYmx1ZSkgKwogIGdlb21fcmliYm9uKGFlcyh5bWluPWNvcl9wcl9sYix5bWF4PWNvcl9wcl91YiksY29sb3I9TkEsZmlsbD1jb3JhbmdlLGFscGhhPTAuMikrCiAgZ2VvbV9saW5lKGFlcyh5PWNvcl9wciksY29sb3I9Y29yYW5nZSkrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEpKSsKICBsYWJzKHk9IkNvcnJlbGF0aW9uIHdpdGggY29tbXVuaXR5IGxldmVsIHNlcm9wcmV2YWxlbmNlIix4PSJTYW1wbGUgc2l6ZSBwZXIgY29tbXVuaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQpzc2NvcnAKCmBgYAoKUGxvdCBhIGZpZ3VyZSBvZiB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgc2Vyb2NvbnZlcnNpb24gcmF0ZSBlc3RpbWF0ZWQgaW4gdGhlIGZ1bGwgc2FtcGxlIHZlcnN1cyBjbHVzdGVyIGxldmVsIG1lYW4gKGJsdWUpIG9yIGNsdXN0ZXIgbGV2ZWwgc2Vyb3ByZXZhbGVuY2UgKG9yYW5nZSkgZXN0aW1hdGVkIGF0IGVhY2ggc21hbGxlciBzYW1wbGUgc2l6ZQoKYGBge3IgZG93bnNhbXBsZSBjb3JyIGZvaSBmaWd1cmV9CnNzY29yZm9pcCA8LSBnZ3Bsb3QoZGF0YT1zc2NvcnJfZm9pLGFlcyh4PXNzKSkgKwogIGZhY2V0X2dyaWQoLn5hbnRpZ2VuZikrCiAgZ2VvbV9yaWJib24oYWVzKHltaW49Y29yX211X2xiLHltYXg9Y29yX211X3ViKSxjb2xvcj1OQSxmaWxsPWNibHVlLGFscGhhPTAuMikrCiAgZ2VvbV9saW5lKGFlcyh5PWNvcl9tdSksY29sb3I9Y2JsdWUpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbj1jb3JfcHJfbGIseW1heD1jb3JfcHJfdWIpLGNvbG9yPU5BLGZpbGw9Y29yYW5nZSxhbHBoYT0wLjIpKwogIGdlb21fbGluZShhZXMoeT1jb3JfcHIpLGNvbG9yPWNvcmFuZ2UpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwxKSkrCiAgbGFicyh5PSJDb3JyZWxhdGlvbiB3aXRoIGNvbW11bml0eSBsZXZlbCBzZXJvY29udmVyc2lvbiByYXRlIix4PSJTYW1wbGUgc2l6ZSBwZXIgY29tbXVuaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQpzc2NvcmZvaXAKCmBgYAoKQmFzZWQgb24gdGhlc2UgZmlndXJlcywgdGhlcmUgaXMgbm8gbG9zcyBvZiBpbmZvcm1hdGlvbiBmcm9tIHJlZHVjaW5nIHF1YW50aXRhdGl2ZSBtZWFzdXJlcyB0byBzZXJvcHJldmFsZW5jZSwgYW5kIGlmIGFueXRoaW5nIHNlcm9wcmV2YWxlbmNlIHBlcmZvcm1zIHNsaWdodGx5IGJldHRlciB3aXRoIHJlc3BlY3QgdG8gY29ycmVsYXRpb24gbWVhc3VyZXMuCgoKCgoK